MVVM: How to show a dialog box from the ViewModel using Behaviors

6 בפברואר 2011

תגיות: , , ,
18 תגובות

Behaviors seems like the killer feature for every possible problem we used to have with MVVM. Thank god for WPF4 Smile

Showing a Message Box is one of those navigational problems that comes with MVVM. It’s something that we want the ViewModel to control, but yet we don’t want the ViewModel to contain any hard reference to the View.

My solution uses the Messenger class from MVVMLight toolkit, but can easily be adjusted to use Prism’s EventAggregator.

Lauren Bugnion suggested a solution a while ago which is to send a message to the view’s CodeBehind, and from there to open a message box like so:

   1: public MainPage() //Yuck code behind.

   2: {

   3:     InitializeComponent();

   4:  

   5:     Messenger.Default.Register<DialogMessage>(

   6:         this,

   7:         msg =>

   8:         {

   9:             var result = MessageBox.Show(

  10:                 msg.Content,

  11:                 msg.Caption,

  12:                 msg.Button);

  13:  

  14:             // Send callback

  15:             msg.ProcessCallback(result);

  16:         });

  17: }

 

It’s important to say that the solution is basically good, and that the separation of layers is kept.

 

It is, however, far from elegant. It uses code behind, and it is not very reusable. That’s where behaviors come into play – The perfect solutions to such problems. What we have to do is create a Custom MessageBox Behavior that could address that.

 

The Custom Behavior

Behaviors are really easy to implement, all you have to do is to implement Behavior<T>, and override the OnAttached and OnDetached:

   1: class DialogBehavior : Behavior<FrameworkElement>

   2: {

   3:     Messenger messenger = Messenger.Default;

   4:  

   5:     protected override void OnAttached()

   6:     {

   7:         base.OnAttached();

   8:  

   9:         messenger.Register<GalaSoft.MvvmLight.Messaging.DialogMessage>(this, Identifier, ShowDialog);

  10:     }

  11:  

  12:     public string Identifier { get; set; }

  13:     public string Caption { get; set; }

  14:     public string Text { get; set; }

  15:     public MessageBoxButton Buttons { get; set; }

  16:  

  17:     private void ShowDialog(GalaSoft.MvvmLight.Messaging.DialogMessage dm)

  18:     {

  19:         var result = MessageBox.Show(Text, Caption, Buttons);

  20:  

  21:         dm.Callback(result);

  22:     }

  23:  

  24: }

In order to use it we have to put it anywhere in the view, but putting it in the top makes more sense:

   1: <i:Interaction.Behaviors>

   2:     <local:DialogBehavior Caption="MyCap" Text="MyText" Buttons="YesNoCancel" Identifier="mb1"  />

   3:     <local:DialogBehavior Caption="MyCap2" Text="MyText2" Buttons="YesNo" Identifier="mb2"  />

   4: </i:Interaction.Behaviors>

Now comes the cool part. The usage from the ViewModel is as simple as it gets:

   1: Messenger mes = Messenger.Default;

   2:  

   3: mes.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage("Hey", res =>

   4: {

   5:     Debug.WriteLine(res.ToString());

   6: }), "mb1");

   7: mes.Send(new GalaSoft.MvvmLight.Messaging.DialogMessage("Hey", res =>

   8: {

   9:     Debug.WriteLine(res.ToString());

  10: }), "mb2");

 

Using the messenger we send a weak message. The DialogBehavior shows a Dialog based on the how we set it in the view, and the response comes back to the ViewModel. Elegant, isn’t it?

The full code can be found here

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

18 תגובות

  1. Mark7 בפברואר 2011 ב 17:43

    The download link is broken.

    להגיב
  2. elad katz12 בפברואר 2011 ב 3:42

    @Mark

    The link seems to be working as far as I can see.. What message are you getting?

    להגיב
  3. Rupert20 בפברואר 2011 ב 2:01

    I too am having issues downloading the code, in Internet Explorer just get a blank page, and in Chrome a 'broken link' message.

    להגיב
  4. eladkatz25 בפברואר 2011 ב 16:42

    You're both correct… I fixed the link.

    להגיב
  5. PWoodall3 במרץ 2011 ב 23:37

    I have tried to replicate your solution and ran into a lifecycle issue. Whenever the view constructor is called, the OnAttached() method of the DialogBehavior is executed. This registers the message type.

    Where is the unregister? If I navigate away and come back, the OnAttached() is executed and the message type is registered again. This causes a message sent to execute the callback multiple times (similar to adding delegates to an event).

    Is there a way to guarentee that the message type is only registered once?

    להגיב
  6. Leny10 במאי 2011 ב 15:35

    Good idea, Will this work in Windows Phone project?

    להגיב
  7. Gary20 במאי 2011 ב 23:19

    What about the case where the View(s) will be allocated dynamically? For example, the application has 200 very sophisticated views that are each represented by a "Dialog" window (not necessarily a MessageBox). The ViewModel would want to invoke 1 of the 200 Views at any given time. If the ViewModel was aware of the View, it could simple create a new instance of the view (i.e., new MyView1()). I'm looking for a more elegant way of the ViewModel to invoke/create views, where the views are allocated dynamically at runtime, but the ViewModel doesn't really know about the view.

    להגיב
  8. eladkatz2 ביוני 2011 ב 4:42

    @PWoodall – Sure, you just have to subscribe to the event Unloaded in the behavior and unregister all you messages. It's pretty straight forward from there.

    @Gary – The solution i purposed is mainly targeted toward MessageBoxes and not Views. The more general View navigation is a different problem. The best solution for it that I know is the View-Switching technique you can find in Prism, which is loosely based on what i show here (it's based on Attached Properties. I might show that in a future post).

    However, I strongly feel that creating more than 5-10 Windows in an application is a big mistake UX-wise. if all you need is View-Switching, however, then the prism's technique is best.

    @Leny – no reason it shouldn't, though i didn't check it there.

    להגיב
  9. Leon30 באוקטובר 2011 ב 19:36

    This is great!!! Just applied this to my project. Fantasic work.

    THANKS!!!!

    להגיב
  10. David15 בנובמבר 2011 ב 1:19

    Looks like the link is still broken. I'd like to get a look at the whole solution.

    להגיב
  11. kytoni26 בפברואר 2012 ב 11:19

    The download link is broken :( annoying

    להגיב
  12. eladkatz26 בפברואר 2012 ב 21:48

    @kitony

    i fixed the download link.. thanks!

    להגיב
  13. gintarassvalbonas@yahoo.com25 במרץ 2012 ב 12:27

    My way
    _mes.Send(new DialogMessage("Do you want to leave this channel?", res =>
    {
    if (res != MessageBoxResult.OK) return;
    Messenger.Default.Send("GoBackRequest");
    App.IrcClientInstance.RfcPart(Channel.Name);
    }) { Button = MessageBoxButton.OKCancel,Caption = ""}, "mb1");

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



    להגיב
  14. Ingebynenty8 באפריל 2013 ב 21:29

    Hello. And Bye.

    להגיב
  15. Eteddelocon9 באפריל 2013 ב 2:54

    Hello. And Bye.

    להגיב
  16. ViekSeeds21 באפריל 2013 ב 21:13

    Nike Unencumbered TR Change 2 Shield broke of textile materials in strip with seasonal requirements, enhances the warmth and durability, and contemplative constituents, in air max rea
    barren visibility when reflection and DWR (fast tone down stubborn) coating to achieve breathability while, contract out feet wilt in wet weather. Shoe upper welt on the by nature of the salaam suds enhances foot take up the cudgels for and stability.

    Aspect ratio of the Nike Uninhabited shoe Trough conceive provides ductile like bare feet feel and sturdiness of multi-direction action while air max 90 billigt
    retaining grip and shockproof column engrained in training shoes. Lightweight Phylite midsole can bring and masses of durability, which increments the expediency exponentially outsole, which significantly reduces the bulk of the shoe.

    Tail and foot rubber grooves made of environmentally friendly materials, stop to lift its nike air max dam
    multi-directional feel on all kinds of track surfaces.

    להגיב