9

I'm trying to use the mvvm-light messenger capability to open a custom confirm password dialog in my view, triggered by a command in my viewmodel.

I think I understand the usage of Messenger.Default.Register and Messenger.Default.Send.

But how do I get the dialog results back in my viewmodel?

To me the sending seems to be a one way street...

Could someone help a beginner with a small C#/WPF code sample?

Thanks for any help

nabulke
  • 11,025
  • 13
  • 65
  • 114

3 Answers3

18

IMHO it is better to use the NotificationMessageAction<T> as it is cut out for this task.

On the sender side:

var msg = new NotificationMessageAction<MessageBoxResult>(this, "GetPassword", (r) =>
{
    if (r == MessageBoxResult.OK)
    {
        // do stuff
    }
});

Messenger.Default.Send(msg);

And on the receiver side:

Messenger.Default.Register<NotificationMessageAction<MessageBoxResult>>(this, (m) =>
{
    if (m.Notification == "GetPassword") {
        var dlg = new PasswordDialog();
        var result = dlg.ShowDialog();
        m.Execute(result);
    }
});

I believe that this approach is cleaner as it does not create an unnecessary dependency from the View to the ViewModel (although this way round is not so bad). For better readability consider sub-classing the NodificationMessageAction<MessageResult>. I.e.

public class ShowPasswordMessage : NotificationMessageAction<MessageBoxResult>
{
    public ShowPasswordMessage(object Sender, Action<MessageBoxResult> callback)
        : base(sender, "GetPassword", callback)
    {

    }
}

Then the sender

var msg = new ShowPasswordMessage(this, (r) =>
{
    if (r == MessageBoxResult.OK)
    {
        // do stuff
    }
});

Messenger.Default.Send(msg);

and receiver side

Messenger.Default.Register<ShowPasswordMessage>(this, (m) =>
{
    var dlg = new PasswordDialog();
    var result = dlg.ShowDialog();
    m.Execute(result);
});

becomes a lot clearer.

And verry important unregister the recipient as else you might create a memory leak.

AxelEckenberger
  • 16,628
  • 3
  • 48
  • 70
  • 2
    +1: your answer helped a lot! Thanks for the good explanation. Didn't know about the unregister issue too. Unfortunately I couldn't find any good and complete documentation for mvvm-light, only a few blogs and example code. – nabulke Jun 22 '11 at 15:26
  • Works great. One small error: it should be 'var msg = new ShowPasswordMessage(this, (r) =>' on the sender side. Perhaps you can edit it for future reference (I don't have enough privileges) – nabulke Jun 22 '11 at 19:01
  • 1
    @nabulke: Laurent's blog at http://www.galasoft.ch has some good examples. Also his MIX 2010 video is a must for starting mvvm light. Post edited. – AxelEckenberger Jun 22 '11 at 20:07
  • @AxelEckenberger its a nice answer, but there's a catch, what if someone wants to put the ViewModel layer in a separate assembly? so, using a MessageBoxResult directly in ViewModel needs a reference for System.Windows and that's not the best as far as we want to loose any dependency on UI layer. – Amir Oveisi Jun 23 '17 at 16:42
  • i mean if we are accepting here to use MessageBoxResult directly in out ViewModel layer, so there is easier ways to do this and yet stay open for any future changes using DI and IoC. – Amir Oveisi Jun 23 '17 at 16:44
  • [@Amir Oveisi](https://stackoverflow.com/users/2015037/amir-oveisi]) it was just a quick an dirty approach. However, if you really want to separate the UI and the VM completely in your production code, I would write a custom enum that has the same integer values as MessageBoxResult, cast the MessageBoxResult to this custom enum and then pass this enum to the VM. – AxelEckenberger Jul 05 '17 at 12:22
1

In Register method you can show a dialog and pass the YourViewModel reference.

 Messenger.Default.Register<YourViewModel>(this, "showDialog", viewModel=>
         {
           var dlg = new Dialog();         
           viewModel.Result = dlg.ShowDialog();                                                  
         });

somewhere in your code you can throw Send() message with a reference to YourViewModel like this:

Messenger.Default.Send(viewModel, "showDialog");
Arseny
  • 7,251
  • 4
  • 37
  • 52
  • Messenger.Default.Send() is a asynchronous function, returning immediately, isn't it? So how do I know when the dialog is closed and the result can be queried? – nabulke Jun 22 '11 at 14:50
  • Could I use a callback function or something like that? I'm a little confused. – nabulke Jun 22 '11 at 14:51
  • Send() function will trigger the lambda where your dialog pops up. Since it is modal dialog (ShowDialog method) it will blocks workflow until it's closed. – Arseny Jun 22 '11 at 14:56
0

In order to achieve the above using DialogMessage as the title suggests, one may use the following:

sender side:

void SendMessage(String msgText)
{
    DialogMessage messege = new DialogMessage(msgText, res =>
        {
            callback(res);
        })
    //set more dialog properties using the Initializer
    { Button = MessageBoxButton.OKCancel, Caption = "" };

    Messenger.Default.Send(messege, "mb1");
}

public void callback(MessageBoxResult res)
{
    if (res == MessageBoxResult.OK)
    { /*do something*/ }
}

receiver side:

void Rec()
{
    Messenger.Default.Register<DialogMessage>(
        this, "mb1", msg => ShowDialog(msg));

    Unloaded += Unreg;
}

void ShowDialog(DialogMessage msg)
{
    var result = MessageBox.Show(
        msg.Content,
        msg.Caption,
        msg.Button);
    msg.Callback(result);
}

note the explicit call to the Callback method in the last line of the receiver.

msg.Callback(result);
yanckst
  • 438
  • 4
  • 17
TomerBu
  • 1,483
  • 15
  • 15