A couple weeks ago Agustin ported the Prism’s PopupChildWindowAction for Silverlight to WPF:
As you can read in its blog post, his PopupModalWindowAction had some limitations when defining a custom ChildWindow, mainly because you cannot reuse the same instance of a Window after it’s closed. Talking with Agustin about how we could workaround that limitation, we came to the idea of passing custom views instead of custom windows and I decided to extend his PopupModalWindowAction to include this functionality along with other ones.
You can find a sample with the new PopupWindowAction at the end of the post.
The main difference between this implementation and the aforementioned one is that the ChildWindow property was replaced with a WindowContent property that can receive FrameworkElements (views) to show in a wrapper Window. As a view can be added and removed from a Window without problems, it can be used more than once.
Another functionality that was added is the possibility to raise the Window as a modal window or not. This can be achieved through the IsModal property of the PopupWindowAction.
Also, you can define if you want the Window to appear right over the view that invoked it or not by setting the CenterOverAssociatedObject property.
Of course, this PopupWindowAction supports the default Windows for the Notification and Confirmation classes in case you only need to show a simple message.
Using custom views
When you raise an InteractionRequest passing a Notification (or Confirmation) that Notification will be automatically set as the DataContext of the Window that will host your custom view. If your view does not have its own DataContext it will inherit it from the host Window, and you will be able to show the Notification’s data. Also, when the host control is closed, the aforementioned Notification will be passed back in the callback to the invoker of the InteractionRequest.
However, this leaves us with some new challenges to be addressed:
- You might want to interact with more complex data in the popup view, which would require the view to have its own view model.
- You might want to communicate the results of your popup view, to the invoker view.
- You might need a way to be able to close the host Window from the view’s view model.
- As you are working with views in Prism, you might want to define regions inside those views.
This PopupWindowAction allows you to address all of this:
You can pass a view model to your view in the following ways:
- Creating a view model that inherits from Notification, resolving it in the invoker view model and passing it as the Notification of the InteractionRequest. This way the popup view will inherit it from the wrapper Window automatically. Also, this view model will be automatically passed back to the invoker in the callback, allowing it to access its data.
- The view could inject its own view model through dependency injection (using the ServiceLocator) which wouldn’t require for the view model to inherit from Notification and would decouple the popup view model from the invoker view model. However, your view and view model would not be able to interact with the Notification passed in the InteractionRequest (I will explain how to solve this in the following section).
Which one you should use will depend mostly of your personal preferences and the requirements of your scenario.
Interacting with the host Window and the Notification
In order for your popup view / view model to interact with the host Window or the corresponding Notification I have created an interface named IPopupWindowActionAware which defines two properties: HostWindow and HostNotification. If your popup view or view model implements the aforementioned interface, the PopupWindowAction will automatically set the aforementioned properties allowing you to access the Window (e.g. you would be able to close it when the user clicks a button) and the corresponding Notification (e.g. you could obtain and return data from / to the invoker view model through it.)
As a side note, take into account that you can also communicate between the view models in a loosely coupled manner by following one of the several approaches mentioned in the following chapter of the Prism documentation:
Using scoped regions
Finally, this PopupWindowAction checks if the popup view has an attached RegionManager or not. If not, a new scoped RegionManager is attached to this view by default in order to handle regions inside the view.
However, regardless if the view already has a RegionManager or if a new one is attached, you need to be able to access to this RegionManager in order to interact with the regions in the view. To solve this, I made this PopupWindowAction to be compatible with an already known interface from my previous posts: IRegionManagerAware. If your view or view model implements this interface, the PopupWindowAction will automatically set the corresponding RegionManager.
Next you can find a sample application which uses the PopupWindowAction in four different ways:
- Showing a modal, centered, default Confirmation popup.
- Showing a non-modal, centered, default Notification popup.
- Showing a modal, centered, custom view which receives its view model as the Notification passed in the InteractionRequest.
- Showing a non-modal, non-centered, custom view which obtains its own view model through the ServiceLocator and has scoped regions.
You can download it from my SkyDrive account under the name PopupWindowActionSample:
I hope that you find it useful!