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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106public
class
DialogService : IDialogService
{
public
virtual
Task ShowPopupAsync<T>(
object
context =
null
, IDictionary<
string
,
object
> settings =
null
)
where
T : IScreen
{
var
rootModel = IoC.Get<T>();
return
ShowPopupAsync(rootModel, context, settings);
}
public
virtual
Task ShowPopupAsync(IScreen rootModel,
object
context =
null
, IDictionary<
string
,
object
> settings =
null
)
{
var
taskCompletionSource =
new
TaskCompletionSource<
bool
>();
var
popup = CreatePopup(rootModel, settings);
var
view = ViewLocator.LocateForModel(rootModel, popup, context);
popup.Child = view;
ViewModelBinder.Bind(rootModel, popup,
null
);
Caliburn.Micro.Action.SetTargetWithoutContext(view, rootModel);
SetPopupPosition(popup, view);
var
activatable = rootModel
as
IActivate;
if
(activatable !=
null
)
{
activatable.Activate();
}
var
deactivator = rootModel
as
IDeactivate;
popup.Closed +=
delegate
{
try
{
rootModel.CloseView =
null
;
if
(deactivator !=
null
)
{
deactivator.Deactivate(
true
);
}
}
finally
{
taskCompletionSource.SetResult(
true
);
}
};
popup.IsOpen =
true
;
return
taskCompletionSource.Task;
}
private
void
SetPopupPosition(Popup popup, UIElement popupContent)
{
var
windowContnt = Window.Current.Content
as
UIElement;
popupContent.Measure(
new
Size(Window.Current.Bounds.Width, Window.Current.Bounds.Height));
popupContent.Arrange(
new
Rect(0, 0, popupContent.DesiredSize.Width, popupContent.DesiredSize.Height));
var
horizontalOffset = Math.Max(0, Window.Current.Bounds.Width / 2 - popupContent.DesiredSize.Width / 2);
var
verticalOffset = Math.Max(0, Window.Current.Bounds.Height / 2 - popupContent.DesiredSize.Height / 2);
popup.HorizontalOffset = horizontalOffset;
popup.VerticalOffset = verticalOffset;
}
protected
virtual
Popup CreatePopup(
object
rootModel, IDictionary<
string
,
object
> settings)
{
var
popup =
new
Popup();
popup.IsLightDismissEnabled =
true
;
ApplySettings(popup, settings);
return
popup;
}
bool
ApplySettings(
object
target, IEnumerable<KeyValuePair<
string
,
object
>> settings)
{
if
(settings !=
null
)
{
var
type = target.GetType();
foreach
(
var
pair
in
settings)
{
var
propertyInfo = type.GetPropertyCaseInsensitive(pair.Key);
if
(propertyInfo !=
null
)
{
propertyInfo.SetValue(target, pair.Value,
null
);
}
}
return
true
;
}
return
false
;
}
}
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.