There are tons and tons of articles around the internet about this topic, but I just can't wrap my head around it. Most articles use code behind, but I want to stick to "pure" MVVM since I try to learn it. Also, I explicitly don't want to use any other framework (MVVMlight, Ninject...). I just want to stick to what WPF has to offer. I know this got asked a lot, but what I found either was not mvvm or was not specific enough.
My task is simple: I want to see the most simple solution of opening a modal dialog, send it a string, and get a string from the dialog back upon closing it.
Therefore I set up my MainWindow.xaml with a text input field (TextBox), a button (that should open the modal dialog) and a textblock that will show the message I intend to receive from the dialog.
The dialog has a TextBlock, showing the user-input from MainWindow.xaml, and a TextBox to enter some text, and a button. You guessed it: you press the button, and the message I typed into the textfield get's returned to MainWindow.xaml. Please refer also to the images I've included - I think it's pretty self-explanatory.
MainWindow.xaml
<Window x:Class="Dialogs.MainWindow"
...
Title="First View (Main Window)" Height="240" Width="630">
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="Main View sayz: "/>
<TextBox Width="360" Margin="10,0,0,30"/>
</StackPanel>
<Button Content="Send to Second View" Command="{Binding SendToSecondViewCommand}" Width="200"/>
<StackPanel Orientation="Horizontal" Margin="10,30,10,10">
<TextBlock Text="Second View replies: "/>
<TextBlock Width="360"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
SecondView.xaml
<UserControl x:Class="Dialogs.SecondView"
...
d:DesignHeight="240" d:DesignWidth="630" Background="BlanchedAlmond">
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="This is what First View sayz: "/>
<TextBlock Width="360"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="Second View replies: "/>
<TextBox Width="360" Margin="10,0,0,30"/>
</StackPanel>
<Button Content="Reply to First View" Command="{Binding ReplyToFirstViewCommand}" Width="200"/>
</StackPanel>
</Grid>
</UserControl>
Here is how I implemented INotifyPropertyChanged (It's actually a .cs file named BaseClasses; I know it's not named properly...)
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged<T>(ref T variable, T value,
[CallerMemberName] string propertyName = null)
{
variable = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And here my base class for relay commands:
public class CommandDelegateBase : ICommand
{
public delegate void ExecuteDelegate(object parameter);
public delegate bool CanExecuteDelegate(object paramerter);
private ExecuteDelegate execute;
private CanExecuteDelegate canExecute;
public CommandDelegateBase(ExecuteDelegate _execute, CanExecuteDelegate _canExecute = null)
{
execute = _execute;
canExecute = _canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return canExecute?.Invoke(parameter) ?? true;
}
public void Execute(object parameter)
{
execute.Invoke(parameter);
}
}
Lastly my ViewModels: FirstViewModel:
public class FirstViewViewModel: NotifyPropertyChangedBase
{
private string _sendText;
public string SendText
{
get { return _sendText; }
set
{
_sendText = value;
OnPropertyChanged(ref _sendText, value);
}
}
public ICommand SendToSecondViewCommand { get; set; }
public FirstViewViewModel()
{
SendToSecondViewCommand = new CommandDelegateBase(SendExecuteCommand, SendCanExecuteCommand);
}
private bool SendCanExecuteCommand(object paramerter)
{
return true;
}
private void SendExecuteCommand(object parameter)
{
//Do stuff to :
// a) show the second view as modal dialog
// b) submit what I just wrote (SendText)
}
}
SecondViewModel:
public class SecondViewViewModel : NotifyPropertyChangedBase
{
private string _replyText;
public string ReplyText
{
get { return _replyText; }
set
{
_replyText = value;
OnPropertyChanged(ref _replyText, value);
}
}
public ICommand ReplyToFirstViewCommand { get; set; }
public SecondViewViewModel()
{
ReplyToFirstViewCommand = new CommandDelegateBase(ReplyExecuteCommand, ReplyCanExecuteCommand);
}
private bool ReplyCanExecuteCommand(object paramerter)
{
return true;
}
private void ReplyExecuteCommand(object parameter)
{
//Do stuff to :
// a) close the second view
// b) reply what I just wrote (ReplyText) back to First View.
}
}
I have a folder called "Models" in my solution but for the sake of simplicity it's empty.
I know there are solutions with helper classes or services - what ever pertains mvvm will do. I also do know that doing this for such a simple task as what I want is quiet "overkill", and has a lot more writing code coming with it than it would be justifyable for this purpose. But again: I'd like to learn this, and understand what I am doing.
Thank you so much in advance!