When you want to display a dislog screen in windows store apps, you can use Flyout, MessageDialog or Popup
the first two should be used for the simple cases – message prompts, question, basic user interaction.
But if you need to display a richer dialog then Popup is the right fit.
The MVVM way to open those dialogs is by using some kind of DialogService that will be called from you ViewModels.
in all the sample i’ve seen on using Popup it always has a strong coupling to the View itself, meaning the Popup was embedded inside the view layout and was triggered to appear by changing the IsOpen property to true.
I wanted to create a service that creates the popup for me, no matter from where i call it. for me, the right approach is to open the popup above the application Frame.
Since im a Caliburn.Micro fan and since Caliburn is the framework with i did it in Caliburn style.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106publicclassDialogService : IDialogService{publicvirtualTask ShowPopupAsync<T>(objectcontext =null, IDictionary<string,object> settings =null)whereT : IScreen{varrootModel = IoC.Get<T>();returnShowPopupAsync(rootModel, context, settings);}publicvirtualTask ShowPopupAsync(IScreen rootModel,objectcontext =null, IDictionary<string,object> settings =null){vartaskCompletionSource =newTaskCompletionSource<bool>();varpopup = CreatePopup(rootModel, settings);varview = ViewLocator.LocateForModel(rootModel, popup, context);popup.Child = view;ViewModelBinder.Bind(rootModel, popup,null);Caliburn.Micro.Action.SetTargetWithoutContext(view, rootModel);SetPopupPosition(popup, view);varactivatable = rootModelasIActivate;if(activatable !=null){activatable.Activate();}vardeactivator = rootModelasIDeactivate;popup.Closed +=delegate{try{rootModel.CloseView =null;if(deactivator !=null){deactivator.Deactivate(true);}}finally{taskCompletionSource.SetResult(true);}};popup.IsOpen =true;returntaskCompletionSource.Task;}privatevoidSetPopupPosition(Popup popup, UIElement popupContent){varwindowContnt = Window.Current.ContentasUIElement;popupContent.Measure(newSize(Window.Current.Bounds.Width, Window.Current.Bounds.Height));popupContent.Arrange(newRect(0, 0, popupContent.DesiredSize.Width, popupContent.DesiredSize.Height));varhorizontalOffset = Math.Max(0, Window.Current.Bounds.Width / 2 - popupContent.DesiredSize.Width / 2);varverticalOffset = Math.Max(0, Window.Current.Bounds.Height / 2 - popupContent.DesiredSize.Height / 2);popup.HorizontalOffset = horizontalOffset;popup.VerticalOffset = verticalOffset;}protectedvirtualPopup CreatePopup(objectrootModel, IDictionary<string,object> settings){varpopup =newPopup();popup.IsLightDismissEnabled =true;ApplySettings(popup, settings);returnpopup;}boolApplySettings(objecttarget, IEnumerable<KeyValuePair<string,object>> settings){if(settings !=null){vartype = target.GetType();foreach(varpairinsettings){varpropertyInfo = type.GetPropertyCaseInsensitive(pair.Key);if(propertyInfo !=null){propertyInfo.SetValue(target, pair.Value,null);}}returntrue;}returnfalse;}}
One intersting thing about Popup is that it supports light dismiss, meaning i can close the popup by clicking outside its region, I decide to turn this option by default, you can always override it by passing false inside the settings dictionary.
Caliburn knows how to make the connection between the IScreen.TryClose() method and the Popup IsOpen property so i dont need to handle it by myself.