I found a small example that uses MVVM Light to show a message to the user. How it uses MVVM Light, I guess, is that it respects the MVVM pattern.
The view code behind:
namespace DialogosPruebas
{
/// <summary>
/// Lógica de interacción para MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(
this,
msg =>
{
var result = MessageBox.Show(
msg.Content,
msg.Caption,
msg.Button);
// Send callback
msg.ProcessCallback(result);
});
}
}
}
And the ViewModel
is:
using System;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace DialogosPruebas.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
/// </para>
/// <para>
/// You can also use Blend to data bind with the tool's support.
/// </para>
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private const string Login = "abcd1234";
public RelayCommand<string> CheckLoginCommand
{
get;
private set;
}
/// <summary>
/// The <see cref="Message" /> property's name.
/// </summary>
public const string MessagePropertyName = "Message";
private string _message = "Login";
/// <summary>
/// Gets the Message property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string Message
{
get
{
return _message;
}
set
{
if (_message == value)
{
return;
}
_message = value;
RaisePropertyChanged(MessagePropertyName);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
CheckLoginCommand = new RelayCommand<string>(CheckLogin);
}
private void CheckLogin(string text)
{
if (text == Login)
{
var message = new DialogMessage("Login confirmed, do you want to continue", DialogMessageCallback)
{
Button = MessageBoxButton.OKCancel,
Caption = "Continue?"
};
Messenger.Default.Send(message);
}
}
private void DialogMessageCallback(MessageBoxResult result)
{
if (result == MessageBoxResult.OK)
{
Message = "Continue";
}
else
{
Message = "Stop";
}
}
}
}
The AXML:
<Window x:Class="DialogosPruebas.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>
<Grid>
<StackPanel x:Name="LayoutRoot" Background="Black">
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
Text="{Binding Message}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" Margin="0,10" />
<TextBox x:Name="LoginTextBox" TextWrapping="Wrap" Margin="10,0" FontSize="21.333" Text="Enter login">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<cmd:EventToCommand Command="{Binding CheckLoginCommand, Mode=OneWay}" CommandParameter="{Binding Text, ElementName=LoginTextBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock TextWrapping="Wrap" Text="(Enter abcd1234 to trigger the message)" HorizontalAlignment="Center" Margin="0,10,0,0" FontSize="16" Foreground="White"/>
</StackPanel>
</Grid>
</Window>
Well, my doubt is, in the code behind the view we use MessageBox
and the ViewModel
has this code:
var message = new DialogMessage("Login confirmed, do you want to continue", DialogMessageCallback)
{
Button = MessageBoxButton.OKCancel,
Caption = "Continue?"
};
Messenger.Default.Send(message);
This sends a request to the view, to the code behind, that then uses MessageBox
.
Why is this better than this solution, which uses MessageBox
in the ViewModel
directly, like this:
private void CheckLogin(string text)
{
if (text == Login)
{
MessageBox.Show("Login correct");
}
}
?
What is the difference? In both cases I use MessageBox
and I have to wait for the user's response.
I have read that to use MessageBox
in the viewModel
is not a good idea, but I don't see what the difference is in this case.
I see two reasons why this approach might be desirable:
1 - Your ViewModels need to be Unit Tested.
raising modal dialogs such as a MessageBox
leads to all sorts of problems in Unit Tests. The decoupled Messenger
approach is safe because in a Unit Test, either no one is listening to the messages, or there is a mocked listener that just returns "Yes" for all user-facing prompts.
2 - Your ViewModels are supposed to be reused on other platforms.
Don't worry about this too much if you're targeting Windows (WPF) only.
The main concern that leads to complete separation from UI is if you will reuse your ViewModels in other platforms.
For example, there's no MessageBox.Show()
in Android, therefore, if you intend to reuse your ViewModel's "Application Logic" there you will need to abstract that code away and provide platform-specific code on each case.
If none of these are important for you, then IMO it's perfectly fine to raise MessageBoxes in your ViewModels, as well as other View-specific issues (such as Window closing) which might be overly complex given the abstractions required by MVVM, for no gain.
The difference is that by sending a message (DialogMessage) your ViewModel is asking the View to show a message. How the message is actually displayed depends on the View. In this case the View will show a simple MessageBox but it could use a UserControl to show a custom dialog.
Using a message the ViewModel doesn't need to know how the message will be displayed so it's still decoupled from the View.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With