WP7 Syncronous MessageBox

Topics: Getting Started
Aug 8, 2011 at 1:54 PM

Hi,

Is there any way make current implementation of Dialog or Popup work synchronous?

i have found following example that works fine:

AutoResetEvent waiteHanlde = new AutoResetEvent(false);
bool res=false;

Guide.BeginShowMessageBox(
"", 
"Pick a version of Windows.", 
new List<string> { "Vista", "Seven" }, 
0, MessageBoxIcon.Error,
asyncResult => { int? returned = Guide.EndShowMessageBox(asyncResult); res = true; waiteHanlde.Set(); }, null); 


return res;

But once i replace Guide.BeginMessageBox with Calibirn`s this.WindowsManager.ShowDialog(datamodel); nothin happens, it simply dead locks. Why? whts the differece between BeginMessageBox and ShowDialog by Cliburn? Any way to make ShowDialog synchronous?

Main thing for me - i need display image and textblock in popup...

Thanks for help!

Aug 8, 2011 at 3:01 PM
Edited Aug 8, 2011 at 3:03 PM

As far I know, it's not possible to create a new DispatcherFrame in SL or WP7, which is the mechanism used by WPF (for the native MessageBox) to avoid freezing the UI.
BeginShowMessageBox is part of Xna Framework, so it probably uses a separated thread to leave the Dispatcher free.

I would use a coroutine with a custom ShowDialogResult (see http://caliburnmicro.codeplex.com/discussions/250303).
This way you can wait for the dialog closing without blocking the UI.

Aug 8, 2011 at 4:10 PM

Thanks for help!

i am not sure i understand what u mean and how can coroutine help in my case. Could you please put some more details on it?

 

Here what i have right now talkin about coroutines:

a basic DialogManager class:

    public class DialogManager : IResult
    {
        private static readonly ILog Log = LogManager.GetLog(typeof(DialogManager));
        private readonly IWindowManager WindowManager;

        readonly Type dialogType;
        readonly string name;
        readonly Action<object, DialogResultEventArgs> callback;


        public DialogManager(string name, Action<object, DialogResultEventArgs> callback)
        {
            this.WindowManager = IoC.Get<IWindowManager>();

            this.name = name;
            this.callback = callback;
        }

        public DialogManager(Type dialogType, Action<object, DialogResultEventArgs> callback)
        {
            this.WindowManager = IoC.Get<IWindowManager>();

            this.dialogType = dialogType;
            this.callback = callback;
        }

        public void Execute(ActionExecutionContext context)
        {
            var theObject = !string.IsNullOrEmpty(name)
                ? IoC.Get<object>(name)
                : IoC.GetInstance(dialogType, null);
            // Needs to be a screen so we can close it
            Screen theScreen = theObject as Screen;
            if (null == theScreen)
            {
                throw new InvalidOperationException();
            }
            // Needs to be an IDialog so we can hook on the Completed event
            IDialog theDialog = theObject as IDialog;
            if (null == theDialog)
            {
                throw new InvalidOperationException();
            }

            theDialog.Completed += new EventHandler<DialogResultEventArgs>(OnDialogCompleted);
            WindowManager.ShowDialog(theDialog, null);
        }

        void OnDialogCompleted(object sender, DialogResultEventArgs e)
        {
            callback(sender, e);

            Screen theScreen = sender as Screen;
            if (null == theScreen) throw new ArgumentException("sender");
            theScreen.CanClose(a =>
            {
                if (!a) return;
            });
            theScreen.TryClose();

            ((IDialog)sender).Completed -= new EventHandler<DialogResultEventArgs>(OnDialogCompleted);

            ResultCompletionEventArgs args = new ResultCompletionEventArgs();
            args.WasCancelled = e.DialogResult==null;
            args.Error = e.Error;
            Completed(this, args);
        }

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };

        public static DialogManager Of<T>(Action<object, DialogResultEventArgs> callback)
        {
            if (!typeof(Screen).IsAssignableFrom(typeof(T))) throw new ArgumentException(string.Format("{0} needs to inherit from {1}", typeof(T).Name, typeof(Screen).Name));
            if (typeof(T).GetInterface(typeof(IDialog).Name, false) == null) throw new ArgumentException(string.Format("{0} needs to implment {1}", typeof(T).Name, typeof(IDialog).Name));
            return new DialogManager(typeof(T), callback);
        }

        public static DialogManager Named(string name, Action<object, DialogResultEventArgs> callback)
        {
            return new DialogManager(name, callback);
        }
    }

And some peace of code i use it from:

 bool res=false;
            AutoResetEvent waiteHandle = new AutoResetEvent(false);
            var test=DialogManager.Of<MyViewModel>(
                (sender, args) =>
                {
res=true;
 waiteHandle.Set();
                }
            );

            test.Execute(null);

            waiteHandle.WaitOne();

            return false;

Either i do somethin wrong or just dont understand logic. 

Thank you!

Aug 8, 2011 at 10:18 PM
Edited Aug 8, 2011 at 10:21 PM

Sorry, I should have elaborate a little more :-)
The point of coroutines is being able to mix, in the same block of code, synchronous code with asynchronous operations.
Let's see my sample from the linked discussion (I'm using the original ShowDialog class, with no callback):

 

public class MyViewModel {

	public IEnumerable<IResult> SomeAction() {
	
		var openDialog = ShowDialog.Of<SomeDialogViewModel>();
		yield return openDialog;
		
		var result = ((SomeDialogViewModel)openDialog.Dialog).TheDialogResult;
		
		//use result
	}

}

The "yield return" statement feeds the enumerator with a previously created instance of IResult (openDialog), then the execution of the coroutine is "paused".
The IResult encapsulate a long running/asynchronous operation, which is run under the control of the CM's infrastructure.
In our sample, the operation starts with the opening of the dialog and ends when the dialog is closed.
The particular IResult implementation (ShowDialog), indeed, raises the Completed event when the dialog VM gets deactivated and closed.

Once the IResult execution is completed, CM resumes the coroutine execution from the point where it halted (just after the "yield return"); this part could be synchronous code.
Since that code is only executed _AFTER_ the IResult completion, you have safe access to the "result" of the dialog (if needed).

To make things work like this, the "SomeAction" action should be launched from the UI (via convention or cal:Message.Attach) or you have to call Coroutine.BeginExecute(SomeAction().GetEnumerator()).

As long as I understand you scenario, using this tecnique you should no longer need a wait handle (which I guess was just intended to wait for dialog closing before continuing with the subsequent code).
Hope it helps; feel free to contact me here if this approach doesn't fit your scenario.