Search code examples
c#wpfcommandmvvm-lightseparation-of-concerns

MVVMLight: Ask information/confirmation in a command?


I've created my first MVVMLight project, and I've a question:

I've a button, on which is bound a command. When the command execute, in different use cases, I've to get/give information to the enduser, like:

  • Ask where the project should be saved if the project is new
  • Give a confirmation that everything has been correctly saved

I know that I can do a MessageBox.Show/... but where? Because in regards of the separation of concerns I guess it should be in the ViewModel? So what is the mecanism in place that I should use for this?

My ViewModel is basically like this:

public class MainViewModel : BaseViewModel
{
    private static readonly Logger m_logger = LoggerProvider.GetLogger("MyPath.MainViewModel");
    private ISerializationService m_serializationService;
    public ICommand TrySaveCommand { get; set; }

    //Lot of other fields here

    public MainViewModel()
    {
        m_serializationService = ServiceLocator.Current.GetInstance<ISerializationService>();
        TrySaveCommand = new RelayCommand(TrySave);
    }
    private void TrySave()
    {
        DispatcherHelper.RunAsync(() =>
        {           
            //Here I need to get the path where I save on some condition 
            m_serializationService.SaveProject(pathIGotFromTheUser);
            //Give a feedback that everything has been correctly saved(for test purpose, a MessageBox.Show() )
        });
    }
}

So how should I do to get information from the user on the file to save? (with SaveFileDialog ) and display that it has correctly been saved (with MessageBox.Show)

Thank you


Solution

  • Laurent Bugnion in his mvvmlight library introduced a very handy helper class called Messenger, using it you can send and receive notifications and/or information between the viewmodels, views or viewmodel/view. here how it works

    • using the Messenger.Default.Send<..>(..) the viewmodel broadcast a message,
    • that message will be intercepted by the any view or viewmodel registered to it (using Messenger.Default.Register<>(..)) and based on that notification an appropriate logic is executed (showing message or a Dialog box for example ..)

    to apply this in your case you must add some view related logic to your code behind, to show DialogueBox and confirmation messages. MainWndow.xaml.cs

     public partial class MainWindow : Window
    {
    
        public MainWindow()
        {            
            Messenger.Default.Register<NotificationMessage>(this, (m) =>
            {
                switch (m.Notification)
                {
                    case "SaveFile":
                        var dlg = new SaveFileDialog();
                        if (dlg.ShowDialog() == true)
                        {
                            var filename = dlg.FileName;
                            Messenger.Default.Send<String>( filename,"FileSaved");
                        }
                        break;
                    case "WentWell":
                        MessageBox.Show("Everything went well Wohoo");
                        break;
    
                }
            });
        }
    }
    

    here the view will show a dialog box or a confirmation message box based on the broadcast-ed ViewModel notification
    and in the MainWindowViewModel

     public class MainViewModel : ViewModelBase
    {
        private static readonly Logger m_logger = LoggerProvider.GetLogger("MyPath.MainViewModel");
        private ISerializationService m_serializationService;
    
        private RelayCommand _trySaveCommand;
        public RelayCommand TrySaveCommand
        {
            get
            {
                return _trySaveCommand
                    ?? (_trySaveCommand = new RelayCommand(
                    () =>
                    {                        
                        Messenger.Default.Send(new NotificationMessage("SaveFile"));    
                    }));
            }
        }
    
        public MainViewModel()
        {
            m_serializationService = ServiceLocator.Current.GetInstance<ISerializationService>();
            Messenger.Default.Register<string>(this, "FileSaved", (pathIGotFromTheUser) =>
            {
                m_serializationService.SaveProject(pathIGotFromTheUser);
                //Give a feedback that everything has been correctly saved(for test purpose, a MessageBox.Show() )
                Messenger.Default.Send<NotificationMessage>(new NotificationMessage("WentWell"));
            });           
        }
    

    the TrySaveCommand associated to a button or whatever will trigger the view.

    **and before you said it, i don't believe that you are violating any mvvm rule by doing that, showing message boxes or dialogues is a presentation related logic and should be handled in the Views part of your solution; the view model by sending a message don't actually know any think about the view, he simply do some work and broadcast a status message, and finally here some in depth information about the Messenger Class HERE.