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

    Published by dcherubini on May 24th, 2012 2:01 pm under Prism, Prism 4, Prism 4.1

    10 Comments

    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.

  • 10 Comments:

    1. Prism: InteractionRequest and PopupModalWindowAction for WPF applications | Agustín Adami said on 2012/05/24:

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

    2. Ronny said on 2012/09/10:

      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

    3. dcherubini said on 2012/09/17:

      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

    4. Mathieu A. said on 2012/09/28:

      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.

    5. dcherubini said on 2012/10/17:

      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

    6. Simon said on 2012/12/22:

      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

    7. dcherubini said on 2013/01/02:

      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

    8. Tim Heney said on 2013/01/24:

      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

    9. Wes Johnson said on 2013/05/17:

      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′ lib\Microsoft.Practices.Prism.UnityExtensions.dll AutoTesterShell

    10. dcherubini said on 2013/05/20:

      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

    Leave a comment

    Your email address will not be published.