PopupWindowAction: using custom views instead of windows in WPF and Prism

Hi everybody,

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.

PopupWindowAction

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:

View models

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.

Note: Take into account that in this case we are not using the RegionManagerRegistrationBehavior for which the aforementioned interface was initially designed for and we have not tested if this PopupWindowAction is compatible with it or not.

Sample application

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!

Note: Please remember that this code is provided “AS IS” with no warranties and confers no rights.



16 Comments

  • […] PopupWindowAction: using custom views instead of windows in WPF and Prism […]

  • Ronny says:

    Hi,
    i followed the instructions from the PopupWindowActionSample.
    This works fine for me.

    One thing:
    my View X which has the InteractionRequestTrigger in its markup is running into the constructor of the model of the popup window on startup.
    On my understanding it should just happened when i click the button which has the respective Command defined.
    If i debug i see from the call stack that my code runs into the code behind of PopupWindowAction() right after the code reaches the markup of my view X.
    What am i missing?
    Thx for any hint..
    Regards, Ronny

  • dcherubini says:

    Hi Ronny,
    I am glad you found this useful.
    Based on my understanding, what you describe is the normal behavior when using XAML.
    As far as I know, when a XAML view is parsed and created, all the elements that are defined in the view’s XAML are also created: if it contains a <Button> a button will be created, if you have a <PopupWindowAction> it will also be created, if you have a <ViewX> it will be created too (regardless if it is inside of an InteractionRequestTrigger or not), etc. Therefore, your ViewX is created when its owner view is created and not when the InteractionRequest is raised.
    What’s more, when you raise the InteractionRequest, the PopupWindowAction will only show the specified view in a new window; it will not create a new instance of it. This means the view is only created once (at the same time that its owner view) and it’s reused for each InteractionRequest.
    I hope this helps,
    Damian Cherubini

  • Mathieu A. says:

    Thank this was very helpful. However I don’t like the design the the IPopupWindowActionAware, this force you to have an instance of a Window in your viewmodel, which makes it very hard to unit test and doesn’t respect the MVVM patern.

    To get around the problem I created a IClosableWindow which expose the Close method, and a ClosableWindow that gets the Window and implement the IClosableWindow close method. This way your viewmodel doesn’t know about the Window, and you can mock this object in your unit tests.

  • dcherubini says:

    Hi Mathieu,
    .
    You are right. It seems that I forgot about those details when designing the IPopupWindowActionAware functionality part.
    .
    I like the approach you are describing to solve this. I will try update this post / sample to use your approach when I have some spare time.
    .
    Thanks for your feedback 🙂
    .
    Damian Cherubini

  • Simon says:

    Hi,dcherubini

    use this solution, if there are many popup custom window, would add many InteractionRequestTriggers in the helloword view. Is there a way to avoid it?

    regards
    Simon Cui

  • dcherubini says:

    Hi Simon,
    .
    You are right. When using InteractionRequests and simple Windows (or Views if using the PopupWindowAction of this post,) you will need an InteractionRequestTrigger for each popup Window / View you define. As far as I know, this is the usual approach when using InteractionRequests.
    .
    As a possible workaround, you could define you own custom logic to use a generic InteractionRequestTrigger for several views. For example, you could define an empty Window or view to be shown by the InteractionRequestTrigger and then define the contents of that Window / view in run-time according to the Notification object passed in the InteractionRequest.
    .
    However, if you plan to have several popup views, I believe you might find the RegionPopupBehavior of Prism’s Stock Trader Reference Implementation more useful that an InteractionRequest. The Stock Trader RI is provided with the Prism 4.1 library’s download here: http://compositewpf.codeplex.com/releases/view/55576 .
    .
    Regards,
    Damian Cherubini

  • Tim Heney says:

    How do you implement regions in the popup view? We have an desktop application take opens a view with 4 regions defined, and wish to use navigation. The regions are present and the proper content appears, but the region manager never appears to have to regions added.

    Thanks,

    Tim

  • Did you figure out this error?

    Error 1 Assembly ‘Microsoft.Practices.Prism.UnityExtensions, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ uses ‘Microsoft.Practices.Unity, Version=3.0.1208.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ which has a higher version than referenced assembly ‘Microsoft.Practices.Unity, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ libMicrosoft.Practices.Prism.UnityExtensions.dll AutoTesterShell

  • dcherubini says:

    Hi Wes,
    .
    That error was discussed in the Prism forums a couple of weeks ago:
    http://compositewpf.codeplex.com/discussions/443219
    .
    You can find more information about the issue, as well as possible workarounds in that thread.
    .
    Regards,
    Damian Cherubini

  • Marcus Baffa says:

    Hi Damian,

    I could not access the Skydrive link. Is there any other place to download the code

  • dcherubini says:

    Hi Marcus,
    .
    That is strange, the files in that SkyDrive are shared as public, so once you click on the link you should see a list of .zip files. The sample described in this blog post is in the PopupWindowActionSample.zip file.
    .
    Let me know if you are still unable to download it.
    .
    Regards,
    Damian Cherubini

  • Dush says:

    Hi Damian,
    Great article.
    Is it possible to give an update for this to use in Prism MVVM approach as Mathieu A work around?
    Thanks

    Hi Mathieu A
    Would you be able to give updated source for your MVVM approach ?
    Thanks

  • Marcus Baffa says:

    Hi Damian,

    I could make the Popup work perfectly. I have a view with a button when the button is clicked the Dialog is displayed and returns the correct value.

    The problem is that I want to use the same functionallity for a LogonView. The LogonWindow must appear before any view is injected in my main region. I tried to raise the request from my Shell Window it worked but the ViewModel was not passed to the view.

    How can I do this. Can you help me ?

    Thanks in advance.

  • Ofer says:

    Thanks, a good demo, eventually!

  • Kanjie says:

    Hi, I can’t download the sample in skydrive. It says I don’t have the access to it. Can you provide another link for me to download?

Leave a Reply