RegionMemberLifetime causes Multiple ConfirmationChildWindow pop ups

Topics: Prism v4 - Silverlight 4
May 17, 2011 at 1:58 PM

Hi,

I have an issue with raising an interaction request after navigating back and forth between region member lifetime controlled views. What appears to be happening is that everytime the interaction request is raised a new instance of the ConfirmationChildWindow is associated with the IInteractionRequest property on my view model.

The view model is long lived whilst the view is marked with the RegionMemberLifetime attribute with KeepAlive set to false. If the the interaction request has been raised, navigating away from the view and then back again and re-raising the interaction request causes 2 (or however many times the view has been navigated to) pop-ups to be displayed on top of each other. Setting KeepAlive to true does resolve the problem. Is there a standard approach for handling this situation or a workaround that anyone can suggest?

XAML:

<i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmInteractionRequest}">
            <prism:PopupChildWindowAction>
                <prism:PopupChildWindowAction.ChildWindow>
                    <shared:ConfirmationWindow />
                </prism:PopupChildWindowAction.ChildWindow>
            </prism:PopupChildWindowAction>
        </prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
View Model:
public void CancelOperation(Action<bool?> onCancel)
        {
            // Generic cancel operation
            _confirmInteractionRequest.Raise(
                ConfirmationViewModel.YesNoCancel(StringLibrary.ConfirmAction, StringLibrary.Confirm_Cancel),
                confirm => onCancel(confirm.Confirmation));
        }
May 17, 2011 at 2:58 PM

Hi,

It could be helpful if you could provide us with a repro sample (for example SkyDrive) of your solution, so that we can help you find out the cause of the unexpected behavior you're experiencing.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

 

May 17, 2011 at 4:06 PM

Hi Miguel,

Thanx for coming back to me so quickly. If I have time today I will try and produce a small sample as this forms part of a much larger app. To recreate you would need to follow the below steps:

1. Add a Prism module to a Prism shell application project that includes a single view. The view is marked with the [RegionMemberLifetime(KeepAlive = false)] attribute and is injected with the interface of a simple view model

2. In the simple view model expose an IInteractionRequest property and set this up to use the basic Notification class to raise against

3. In the view's XAML add the Interaction.Triggers element as above and simply omit the child window which will use the NotificationChildWindow by default.

4. In the module init of the new module ensure the registered view model is set up as long-lived i.e. ContainerControlledLifetimeManager

5. In the view add a button that raises the interaction request in the view model

6. Run the application and navigate to the view - clicking the button will display a notification window

7. Navigate to a different module and then back again - click the button again and this time you should recieve 2 notification windows on top of each other

Regards,

Chris

 

May 17, 2011 at 4:36 PM

Hi Miguel,

I have been able to recreate the issue by modifying the Prism quick start solution.

Please find a linkt to the zipped up project here - http://cid-09f71a6daa654209.office.live.com/self.aspx/Public/PrismQuickStart.zip

You may need to fix the Prism references to get it to work, but when you do run it up, click the arrows to edit multiple records and click the Test button at the bottom. You will see for every edit a new notification child window is displayed on top of each other. These all stem from the fact the view model is long-lived whilst the views are disposed using the RegionMemberLifetime attribute.

Any workaround you could suggest would be much appreciated.

Regards,

Chris

May 17, 2011 at 9:44 PM

Hi Chris,

After debugging your application we found that the reason why your notifications popups windows were displayed many times is because your view even they are removed from the Region Manager Region’s collection, they are not disposed and at the same time you configured your view models as singleton.

We modified the solution and uploaded to SkyDrive, you can find it here. The workaround we applied was to create a new instance of the view models every time you navigate to EditView.

On the other hand, on EditView.xaml.cs you will find debugging trace code to verify all the live instances of the view when you stop debugging.

I hope this information helps you.

Thanks,

Miguel Bronzovic
http://blogs.southworks.net/mbronzovic

 

May 18, 2011 at 9:00 AM

Hi Miguel,

Thank you for coming back to me, but the fix you have supplied does not solve my problem. The singleton view models within my application do not hold single objects, but instead hold a lot of additional information relating to collections, selected state of objects etc., etc. They effectively hold the current state of the module. I want the view models to stay as singletons whilst the views are disposed when navigating to a different spoke of the application. This will reduce the memory footprint but ensure that on navigating back to a view the previous state can be restored.

I believe that this is a bug in the Prism framework and that the InteractionRequestTrigger's should be disposed of along with the view.

I have constructed a workaround for this issue, as detailed below, but would like this raised as bug in the framework. Simply detaching the interaction request triggers when the view is disposed resolves the problem:

In code-behind of view:

Unloaded += (o, e) => InteractionHelper.DisposeTriggers(this);

DisposeTriggers method:

public static void DisposeTriggers(Control parentControl)
        {
            var triggers = Interaction.GetTriggers(parentControl);

            foreach (var trigger in triggers.OfType<InteractionRequestTrigger>())
            {
                trigger.SourceObject = null;

                foreach (var action in trigger.Actions.OfType<PopupChildWindowAction>())
                {
                    action.ChildWindow.DataContext = null;
                    action.ChildWindow = null;
                    action.Detach();
                }
                
                trigger.Detach();
            }
        }
Regards,
Chris
Developer
May 19, 2011 at 5:07 PM

Hi Chris,

You could create a work item in the Issue Tracker, notifying the team about this behavior, so that it is considered to be changed on a future Prism release.

Thanks,

Guido Leandro Maliandi
http://blogs.southworks.net/gmaliandi

May 27, 2011 at 8:58 AM

Hi Chris,

thanks for your question and the code you provided to solve this problem

It saved my day and working time

Nov 24, 2011 at 8:46 PM

This solution helped me too.

Thanks Chris!