none
How to make a Silverlight MessageBox.Show that waits until a choice is made ?

    Question

  • I'm wondering how a MessageBox.Show is coded since it returns a value but based on an event according to what the user click on the form.  How the control waits and returns a value by the Show method ?

     

    Monday, January 11, 2010 6:51 PM

Answers

  • I have built many large, scalable applications in Silverlight and having an asynchronous message box is a very robust, scalable pattern. You're looking at it the wrong way because you're trying to place the onus of understanding the "return condition" on the message box, instead of decoupling it.

    What you need is a class that takes a message and a delegate for the positive or negative response. Then each client of the message box can inject the return code as needed.

    Forget MessengerActionType, etc.

    Instead, I would do:

    MessageClass.Message = "Are you sure you wish to delete the video?";
    MessageClass.Response = (r) => { if (r) { ... code to trigger the delete here} };
    MessageClass.Invoke();

    The message class is just a service. I raise an event with some text and provide it with actions based on the positive or negative response. Following sound SOLID principles, the message box class itself isn't aware of the rest of the application and is completely decoupled/independent. Instead, it exposes delegates like this :

    public Action<bool> Reponse { get; set; }

    Then, the message box can display the message. When it is closed or the user clicks something, it simply calls Response(result) and then the calling code can determine how to handle the response. 

    Sunday, January 17, 2010 11:27 AM

All replies

  • Did you try using reflector to get the information?
    Tuesday, January 12, 2010 2:19 AM
  • Hi GearWorld,

    it's a function of the browser. The MessageBox doesn't belong to Silverlight, it belongs to the Browser. (In JavaScript you open it with alert, prompt or confirm). As the MessageBox is opened modal by the Browser, you can't click anything inside of the Browser, so the browser waits for it, and so also Silverlight has to wait for it. :)

     

    Tuesday, January 12, 2010 2:27 AM
  • By the way, here the internal code reflector shows:

     

    private static MessageBoxResult ShowCore(string messageBoxText, string caption, MessageBoxButton button)
    {
        ...    uint type = (uint) button;
        return Win32ToMessageBoxResult((Win32MessageBoxResult) XcpImports.MessageBox_ShowCore(messageBoxText, caption, type));
    }
    
    
     
    Tuesday, January 12, 2010 2:31 AM
  • Oh sorry,  I was talking about a MessageBox I would do myself in SilverLight
    So I want to return the result in the method call but it needs for it to wait there until the user has pressed a button

    I know the MessageBox for Windows but I want to do one in SilverLight and I need to understand how to make the call to the method .Show() waiting there, until the user press OK for insteance and it returns to the caller and continue so I can do things like this :

    if(MySLMessageBox.Show("MyMessage", MYSLBUTTON.OK) == MySLMessageBox.BUTTON.ResultOk)
    {
    }

    When I show a UserControl it doesn't wait there, Silverlight Core continue it's course to the end of the method in which the MessageBox is used.

     

    Tuesday, January 12, 2010 6:08 PM
  • You can also use a ChildWindow for a similar effect.

    Tuesday, January 12, 2010 6:10 PM
  • You'll need to wrap your arms around a bit of asynchronous programming.

    First, you can open a ChildWindow instance which, by default, is modal, so it prevents the user from doing anything else while it is displaying.

    Second, you can specify a callback to trigger your next action once it is clicked. So instead of linear code like this:

    dosomething(a)
    waitformessagebox
    dosomething(b)

    You want to do this:

    dosomething(a)
    showmessagebox(withcallbackwhendone -> dosomething(b))

    Here is a example where I create a special object for message boxes. When I need to show a message, I create the object and tell it what to call once it receives the message. This example uses Event Aggregator but you can just as easily use a service or other implementation.

    http://csharperimage.jeremylikness.com/2009/09/decoupled-childwindow-dialogs-with.html

    Tuesday, January 12, 2010 7:01 PM
  • You can also use a ChildWindow for a similar effect.

    Oh Yes the ChildWindow.  And I presume I can Style it to my needs ?
    This could be more easy then I tought.  Could I add a constructor like the Windows MessageBox ? I mean,
    With the ChildWindow, I dislike the fact that we have to set the .Content prior to call .Show

    If I wrap the ChildWindow in a UserControl.  Will the .Show be MODAL anyway ?
    I didn't look at it yet but can I add the ChildWindow on Blend3 Designer and add things to it ?

    Can we perform simply MODAL UserControl ? If so I would just have to do a UserControl with a Show function that make my UserControl visible in MODAL state ? I don't know I'm just asking.

    Techniclly I don't understand the MODAL state,  I mean, how it's made, is this a CORE element that isn't accessible to us ? It just suspend the UI thread ?

    Wednesday, January 13, 2010 6:04 AM
  • The callback idea isn't very practible.

    I already have one like this,  But each time I go in the callback, I need to know what is the purpose to get back here and there's too much code going on in the call back which I have to differenciate why I do come back. 

    Wednesday, January 13, 2010 6:13 AM
  • I'm not really getting why a callback isn't practical ... it's a very common pattern in programming and by nature, a dialog is asynchronous because it depends on user input. It was created that way for a purpose, to avoid blocking the UI thread to wait for user input.You can simply put what you need in a state object and pass it back and it's the same as blocking/synchrnous code without the overhead. I'm just suggesting it makes more sense to use the existing architecture than to invent a synchrnous call and create all of the overhead of waiting/blocking when it just isn't needed.

    Here's info on customizing the child window:

    http://timheuer.com/blog/archive/2009/05/10/silverlight-childwindow-non-modal-refactor.aspx

    Wednesday, January 13, 2010 7:24 AM
  • Wednesday, January 13, 2010 1:20 PM
  • Thanx for your hint Jeremy, but that's not what I'm looking for, 

    I'm not really getting why a callback isn't practical ... it's a very common pattern in programming and by nature, a dialog is asynchronous because it depends on user input. It was created that way for a purpose, to avoid blocking the UI thread to wait for user input.You can simply put what you need in a state object and pass it back and it's the same as blocking/synchrnous code without the overhead. I'm just suggesting it makes more sense to use the existing architecture than to invent a synchrnous call and create all of the overhead of waiting/blocking when it just isn't needed.

    Here's info on customizing the child window:

    http://timheuer.com/blog/archive/2009/05/10/silverlight-childwindow-non-modal-refactor.aspx

    Wednesday, January 13, 2010 5:23 PM
  • I would love to know the real trick to make it works.

    Having a method like .Show("Message", etc.) that waits and returns the result without any call back and exactly like the Windows MessageBox.Show()

    If it's really impossible and it is confirmed by Microsoft, I will live with the pain of ChildWindow OR my own creation for the rest of my silverlight life.

     

    Friday, January 15, 2010 5:57 AM
  • Hi GearWorld,

    MessageBox.Show opens a MessageBox that belongs to the Browsers Window. This MessageBox is a Modal System Dialog, and such a Modal System Dialog has its own MessageLoop so that Yes or No-Buttons can be clicked, and the result can be send back to the main-(Browser)-Window. Because the Modal System Dialog has its own MessageLoop, the Main-Browser-Window can wait, cause input would be processed by the MessageLoop of the Dialog.

    If you want to implement such a thing inside Silverlight, i think you can't, cause there's just one Dispatcher for the UI-Thread. You would need to create a second one for the Dialog you open, but you can't, as there's just a single instance, a single MessageLoop. So you can't "wait" in a Show-Method till the user clicks something. Instead you have to show your window and your window fires an event when something has be clicked. That's why the ChildWindow works like it works.

    Friday, January 15, 2010 6:11 AM
  • Hi,

    Nothing is impossible ofcourse. But I have to agree with Jeremy Likness on the matter of the ChildWindow. It's been designed with multiple purposes in mind then just a simple true or false. You are free to make your own custom control. The guys have posted a few links which could point to the right direction. Keep up the coding spirit I would say.

    Regards,
    Will

    Friday, January 15, 2010 6:21 AM
  • Hi GearWorld,

    MessageBox.Show opens a MessageBox that belongs to the Browsers Window. This MessageBox is a Modal System Dialog, and such a Modal System Dialog has its own MessageLoop so that Yes or No-Buttons can be clicked, and the result can be send back to the main-(Browser)-Window. Because the Modal System Dialog has its own MessageLoop, the Main-Browser-Window can wait, cause input would be processed by the MessageLoop of the Dialog.

    If you want to implement such a thing inside Silverlight, i think you can't, cause there's just one Dispatcher for the UI-Thread. You would need to create a second one for the Dialog you open, but you can't, as there's just a single instance, a single MessageLoop. So you can't "wait" in a Show-Method till the user clicks something. Instead you have to show your window and your window fires an event when something has be clicked. That's why the ChildWindow works like it works.

     

    Ok so if I do it that way, a childwindow and going back in the call back to see what the user pressed.
    Now what do I do if I have to call this ChildWindow from several places that needs to do different things.
    Could that means I will have a call back with full of if else if with booleans that tells me what to do ?

     

     

    Friday, January 15, 2010 7:32 PM
  • Hi,

    Nothing is impossible ofcourse. But I have to agree with Jeremy Likness on the matter of the ChildWindow. It's been designed with multiple purposes in mind then just a simple true or false. You are free to make your own custom control. The guys have posted a few links which could point to the right direction. Keep up the coding spirit I would say.

    Regards,
    Will

    Thank you for your religious message :)

    Friday, January 15, 2010 7:33 PM
  • Here's what I actually have and it's insane to have to work this way because I will have an event that will be full of conditions according to When I did call the ChildWindow and for what I'm calling it.  Could you imagine in a huge application what that means ?

    Maybe it's because I don't understand something 

    private void Messager_ButtonPressedEvent(object sender, SAPLibrary.ButtonEventArgs e)
    {
       
    if (_MessagerAction == MESSAGER_ACTION.DELETE_FILE_TOO)
        {
            ProxyCode.
    GearVideoItem gvi = agVideos.FocusedDataRow as ProxyCode.GearVideoItem;

            if (gvi != null)
            {
                _Proxy.DeleteVideoAsync(
    ServiceKey(), gvi, e.ButtonPressed == SAPLibrary.BUTTONSTATE.OK);
                VideoItems.Remove(gvi);
                ClearVideoControls();
            }
        }
    }

    Of course this is nothing yet but imagine I have to know if I have to DELETE a file but the code inside it, doesn't concern the firt call but another one.  That means I will need another flag to know what to do when a deletion confirmation is asked to the user.

    OUFFF !

     

    Friday, January 15, 2010 7:40 PM
  • I have built many large, scalable applications in Silverlight and having an asynchronous message box is a very robust, scalable pattern. You're looking at it the wrong way because you're trying to place the onus of understanding the "return condition" on the message box, instead of decoupling it.

    What you need is a class that takes a message and a delegate for the positive or negative response. Then each client of the message box can inject the return code as needed.

    Forget MessengerActionType, etc.

    Instead, I would do:

    MessageClass.Message = "Are you sure you wish to delete the video?";
    MessageClass.Response = (r) => { if (r) { ... code to trigger the delete here} };
    MessageClass.Invoke();

    The message class is just a service. I raise an event with some text and provide it with actions based on the positive or negative response. Following sound SOLID principles, the message box class itself isn't aware of the rest of the application and is completely decoupled/independent. Instead, it exposes delegates like this :

    public Action<bool> Reponse { get; set; }

    Then, the message box can display the message. When it is closed or the user clicks something, it simply calls Response(result) and then the calling code can determine how to handle the response. 

    Sunday, January 17, 2010 11:27 AM
  • I'm ready to give a try but I do not seem to be as expert as you so could you pack a little zip with the source ?
    I probably need the BIG picture. 

    Thank you

    I have built many large, scalable applications in Silverlight and having an asynchronous message box is a very robust, scalable pattern. You're looking at it the wrong way because you're trying to place the onus of understanding the "return condition" on the message box, instead of decoupling it.

    What you need is a class that takes a message and a delegate for the positive or negative response. Then each client of the message box can inject the return code as needed.

    Forget MessengerActionType, etc.

    Instead, I would do:

    MessageClass.Message = "Are you sure you wish to delete the video?";
    MessageClass.Response = (r) => { if (r) { ... code to trigger the delete here} };
    MessageClass.Invoke();

    The message class is just a service. I raise an event with some text and provide it with actions based on the positive or negative response. Following sound SOLID principles, the message box class itself isn't aware of the rest of the application and is completely decoupled/independent. Instead, it exposes delegates like this :

    public Action<bool> Reponse { get; set; }

    Then, the message box can display the message. When it is closed or the user clicks something, it simply calls Response(result) and then the calling code can determine how to handle the response. 

    Sunday, January 17, 2010 2:50 PM
  • I will see what I can do ... busy week for me but it comes up enough that it makes sense to build an example application and a blog post, so I'll work on that this week and update the link here.

    Sunday, January 17, 2010 5:35 PM
  • Awesome.  You're the king Jeremy.

     

    Monday, January 18, 2010 8:43 AM
  • I just read about delegate and implemented it in my object. and all I can say is WOW, this thing is powerful.  I didn't know I was able to delegate a method of the calling object.

    It works like a charm.  Now I can do it the right way, doing the method of my operation according to what the user select and the beauty is that the method is called by the messagebox object. 

    I have so much to learn but actually it works fine.  Probably not the best way but it's all good.

    Thank you for your effort to show me the right path !

     

    Monday, January 18, 2010 9:46 AM
  • Saturday, January 23, 2010 12:36 PM
  • Awesome.  Can't ask more than that.
    I did read the entire article and now I need to practice.

    Thank you

     

    Sunday, January 24, 2010 6:03 AM
  • Here's my final code.  Clever way to code I must admit...

     

    _MessageBox.Show("Are you sure you want to delete this video ?", "Question...", (Confirm) => 
    {
        if (Confirm == SAPLibrary.BUTTON_PRESSED.OK)
        {
            _MessageBox.Show("Delete the file too ?", "Message...", (DeleteFileToo) =>
            {
                ProxyCode.GearVideoItem gvi = agVideos.FocusedDataRow as ProxyCode.GearVideoItem;
    
                if (gvi != null)
                {
                    _Proxy.DeleteVideoAsync(GearObjects.Gear.ServiceKey(), gvi, DeleteFileToo == SAPLibrary.BUTTON_PRESSED.OK);
                    VideoItems.Remove(gvi);
                    ClearVideoControls();
                }
            }, "Yes", "No");
        }
    }, "Yes", "No");
    
     
    Sunday, January 24, 2010 6:32 AM
  •  Great it works now. But it's still not waiting and based on events/delegates in form of lambdas. :)

    Sunday, January 24, 2010 6:34 AM
  • Hi Thomas,

    Check out http://silverlightmsgbox.codeplex.com/. The project allows you to utilize several useful asynchornous Silverlight message boxes i.e. info, error, confirm, text input, etc. Good luck.

    Wednesday, January 19, 2011 3:37 PM
  • What you need is a class that takes a message and a delegate for the positive or negative response. Then each client of the message box can inject the return code as needed.

    Instead of letting the dialog box close itself, I raise an event in the dialog box where the EventArg contains the dialog specific result, and then typically the page handling it closes the dialog and consumes the data anyway it likes.

    Wednesday, January 19, 2011 5:40 PM
  • This all really nice and stuff, but it really ISN'T always scalable and practical. What if you have a situation where you may or may NOT show the dialog?

    For example if you have to notify a user of an error condition.


    try
    {
         DoSomethingThatMayCauseAnError();
    }
    catch(Exception ex)
    {
         DisplayErrorInChildWindow(ex);
    }

    DoSomethingElse();
    AndAnotherThing();


    Suddenly you get into a whole lot of spagetti code, unnecessarily splitting up your functions and having control logic just because SOMETIMES you want to hold off on something.

    Tuesday, May 10, 2011 9:35 AM
  • Agreed - it is difficult to try to follow program flow in an asyncronous system. The program turns into a web of glorified goto statements. 

    Tuesday, May 10, 2011 2:16 PM
  • Sadly, thinking about it, there really can't be a solution. We probably won't see one until SL 6.

    It seems to be a fundamental shortcoming in the design of SL

    Tuesday, May 10, 2011 2:24 PM
  • Hi all,

    I read this thread and this is the exact same problem that I have.  There a nice control made by http://silverlightmsgbox.codeplex.com/  just like discussed above Show() method does not return any value and you have to catch the window's value in the event handler.  This would work great unless you are using a MVVM pattern.  Here is an example what I have:

    This is inside one of my methods in the view model

    // give user the opportunity to cancel the delete, and we need to check for this.
    if (Deleting != null)
    {
     var args = new ModelEventArgs();
     Deleting(this, args);

     //Check if user chose to cancel deletion
            if (args.Cancel)
            {
             return; //do nothing
            }

            //do something here
    }

    This is inside my View

    void MyViewModel_Deleting(object sender, Model.BusinessModelObjects.ModelEventArgs e)
    {
     if (MessageBoxResult.Cancel == MessageBox.Show("Are you sure you want to delete?", "Delete Device Users", MessageBoxButton.OKCancel))
                    e.Cancel = true;
    }

    Basically, ViewModel fires an event, view catches it, shows the messagebox for confirmation, and assigns true to cancel value in the event argument if user clicked on cancel.  Now, my ViewModel checks for the event argument value and can do something. 

    If I were to replace the standard message box with custom message box like the one you are discussing above, my View would not wait for the result from .Show() method, but result would be handled in the event handler, then I would not be able to assign event argument cancel value (e.Cancel = true) in order for ViewModel to evaluate it.

    Does anyone has a suggestion how something like this should be implemented?

    Thursday, May 19, 2011 3:25 PM