How to best add popup to visual tree when using MVVM on WP7
I'm using MVVM via Caliburn Micro on WP7. I have a popup that is shown from the VM. On the popup is a performance progress bar. The progress bar does not show when IsIndeterminate is set to true because the popup is not in the visual tree (it is a custom control).
If I grab the view from the view model and force the popup into the visual tree the progress bar displays correctly. I don't really want to do this though.
What is the best way to do this w开发者_运维知识库hilst preserving the separation of view and view model. Is there some way the popup can insert itself into the root page or frame?
The way I approach this is with a child view model/controller/event source exposed by the view model and bound to the control.
The control can listen for events on the source to do it's work, it's nicely separated and even unit testable.
A good example of this is my Status Indicator Control, the view model exposes a StatusSource with methods such as Display and Clear. The control itself is bound to the source and listens for changes. From there you can do anything, including creating the popup and inserting it into the visual tree.
Are you working with a designer? I've found that doing anything view-based in code, other than loading data, confuses and/or frustrates them. I would recommend creating 2 visual states, one with the popup shown and one hidden, so that the designer can see what the app looks like and can toggle (and style) the popup in Blend. I then use the DataStateBehavior or DataStateSwitchBehavior to control the popup from my VM.
I ended up creating a base popup class and adding this code in:
private void HookIntoPage()
{
if (UserControlHelper.RootVisual != null)
{
PhoneApplicationPage page = (PhoneApplicationPage)UserControlHelper.RootVisual.Content;
// Hook up into the back key press event of the current page
page.BackKeyPress += BackKeyPress;
FrameworkElement element = page.FindVisualChild("MainGrid");
if (element != null && element is Grid)
{
Grid grid = element as Grid;
grid.Children.Add(this);
}
else
{
throw new Exception("Popup cannot find MainGrid");
}
}
}
It's a bit hard coded as it looks for a MainGrid grid control. This could be improved to look for an appropriate top level container.
There are 2 helper classes at play here. UserControlHelper has the following methods:
public static PhoneApplicationFrame RootVisual
{
get
{
return Application.Current == null ? null : Application.Current.RootVisual as PhoneApplicationFrame;
}
}
FindVisualChild comes from a VisualTreeHelperExtension class that came with Phone7.Fx.preview that I use for its bindable app bar.
精彩评论