nach etlichen Versuchen, Tutorials und der oft nicht ganz einfachen MSDN wähnte ich mich bereits am Ziel, mit dem simpelst vorstellbaren Ziel: Ein Hauptfenster, mit zwei verschiedenen GUI-Bereichen, jedes mit eigener View (usercontrol) und eigenem VIewModel. Eines enthält eine Textbox, das andere einen Textblock - was ich in die Textbox eingebe, soll im Textblock zu sehen sein.
Mein Code verwendet keinen Code behind. Ich habe versucht nach meinem Kenntnis- und Wissensstand (gefühlt immer noch Anfänger...) MVVM umzusetzen.
[WPF app, .NET 6]
Ich weiß, meine Frage ist vermutlich ein alter Hund, aber es würde mir sehr helfen, wenn mir jemand aufzeigen könnte an welchem Punkt und warum mein Code sich nicht so verhält, wie von mir beabsichtigt. Ich werde außerdem am Ende kurz meine Gedanken zu einzelnen Codesegmenten darlegen, in der Hoffnung, dass mein Fehler evtl. auf Missverständnissen oder Fehlinterpretationen beruht.
Ohne weitere Worte - hier der Code
1. MainWindowView
<Window x:Class="LearnViewModelsAndViews.Views.MainWindowView"
[...]
<Window.DataContext>
<viewModels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<ContentControl>
<viewModels:RedViewModel/>
</ContentControl>
</Grid>
<Grid Grid.Row="1">
<ContentControl>
<viewModels:BlueViewModel/>
</ContentControl>
</Grid>
</Grid>
</Grid>
</Window>
Und das zugehörige MainWindowViewModel
public class MainWindowViewModel: NotifyPropertyChangedBase
{
private RedViewModel _redVM;
public RedViewModel RedVM
{
get { return _redVM; }
set { OnPropertyChanged(ref _redVM, value); }
}
private BlueViewModel _blueVM;
public BlueViewModel BlueVM
{
get { return _blueVM; }
set { OnPropertyChanged(ref _blueVM, value); }
}
public MainWindowViewModel()
{
RedVM = new RedViewModel();
BlueVM = new BlueViewModel();
SetEvents();
}
private void SetEvents()
{
RedVM.OnInputTextChanged += BlueVM.HandleInputTextChanged;
}
}
}
XAML von RedView
<UserControl x:Class="LearnViewModelsAndViews.Views.RedView"
[...]
<Grid Background="LightCoral">
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Width="220" Height="60" Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>
Und das RedViewModel
public class RedViewModel: NotifyPropertyChangedBase
{
public event EventHandler OnInputTextChanged;
private string _inputText;
public string InputText
{
get { return _inputText; }
set
{
OnPropertyChanged(ref _inputText, value);
OnInputTextChanged?.Invoke(value, EventArgs.Empty);
}
}
public RedViewModel()
{
}
}
Hier die BlueView.xaml
<UserControl x:Class="LearnViewModelsAndViews.Views.BlueView"
[...]
<Grid Background="LightBlue">
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Width="220" Height="60" FontSize="18" Foreground="Red" Text="{Binding DisplayText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</Grid>
</UserControl>
Und das zugehörige ViewModel
public class BlueViewModel: NotifyPropertyChangedBase
{
private string _displayText;
public string DisplayText
{
get { return _displayText; }
set { OnPropertyChanged(ref _displayText, value); }
}
public BlueViewModel()
{
}
public void HandleInputTextChanged(object sender, EventArgs e)
{
DisplayText = sender.ToString();
}
}
Last but not least meine App.xaml
<Application x:Class="LearnViewModelsAndViews.App"
[...]
StartupUri="Views/MainWindowView.xaml">
<Application.Resources>
<DataTemplate DataType="{x:Type viewModels:RedViewModel}">
<views:RedView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:BlueViewModel}">
<views:BlueView/>
</DataTemplate>
</Application.Resources>
</Application>
OK: mein Projekt wird wie vorgesehen erstellt und ich bekomme ein Projektfenster angezeigt mit meinem RedView im oberen Bereich und meiner BlueView im unteren Bereich. Soweit alles paletti.
Gebe ich nun Text in die Textbox der RedView ein, wird das OnPropertyChanged() zwar gefeuert, der Event darunter (OnInputTextChanged) ist jedoch null.
Meine Fragen soweit: wo ist der Denkfehler? Was muss ich ändern?
Die Tatsache, dass der event null ist, interpretiere ich so, dass mein BlueVM nicht das ViewModel ist, das ich anzusprechen beabsichtige. Hier kommt nun (mit ziemlicher Sicherheit) Halbwissen ins Spiel:
- mit den DataTemplates in der App.xaml sage ich doch nichts anderes als, dass wenn das ViewModel vom Typ RedViewModel ist, dann gehört dazu die RedView, bzw. umgekehrt. Sehr vereinfacht und vermutlich ungelenk formuliert. Aber auch völlig falsch?
- ich instanziiere ein RedViewModel (RedVM) und ein BlueViewModel (BlueVM) auf meinem MainViewModel. Da ich die Werteveränderung von InputText per event weiterleiten möchte, platziere ich hier meine Subscription ( RedVM.OnInputTextChanged += BlueVM.HandleInputTextChanged; ) Allerdings werden diese ViewModels vermutlich gar nicht angesprochen: irgendwo "hinter den Kulissen" erstelle ich wohl (unbeabsichtigt) eine andere Instanz von RedViewModel und BlueViewModel - und dann ist klar, dass der Compiler nicht wissen kann welche Instanz jetzt implizit gemeint ist.
Wie gesagt: für konkrete Hinweise bin ich euch sehr dankbar. Ich möchte mir nur ungern ein neues Hobby suchen ;-)
Gruß
Vorph