Multiple instances of a window (view)

Topics: Prism v2 - WPF 3.5
Mar 22, 2009 at 11:43 AM
I have a OrderView and displays this view in a separate window by using RegionPopupBehaviors.cs (from StockTraderRI).
This is my code, using a scoped region:
            m_ShellRegion.Add(view, null, true);
            m_ShellRegion.Activate(view);
(the shellRegion is of type RegionPopupBehaviours)
This works fine, but I am not able display multiple windows at the same time.
Does anybody know how ?

Mar 26, 2009 at 7:15 PM

Hi larserik,

 

Unfortunately the RegionPopupBehavior was designed to show only one popup window at a time. Whenever you change the active view to another the previous window is closed and a new one is opened. You can verify this looking at the DialogActivationBehavior (StockTraderRI.Infrastructure.Behaviors. DialogActivationBehavior) implementation :

 

            private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

            {

            if (e.Action == NotifyCollectionChangedAction.Add)

            {

                this.CloseContentDialog();

                this.PrepareContentDialog(e.NewItems[0]);

            }

            else if (e.Action == NotifyCollectionChangedAction.Remove)

            {

                this.CloseContentDialog();

            }

            }

 

So to be able to show multiple windows at the same time you will need to create a new behavior. I'll try to point out how to do it modifying the existing RegionPopupBehavior and DialogActivationBehavior classes.

 

First you should modify the DialogActivationBehavior class. Change the ActiveViews_CollectionChanged method not to close the active window when adding new elements and when removing elements we need to specify which window to close. The method should look like this (in bold code modified):

     

        private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

        {

            if (e.Action == NotifyCollectionChangedAction.Add)

            {

                //this.CloseContentDialog();

                this.PrepareContentDialog(e.NewItems[0]);

            }

            else if (e.Action == NotifyCollectionChangedAction.Remove)

            {

                this.CloseContentDialog(((FrameworkElement)e.OldItems[0]).Parent as Window);

            }

        }

 

Something similar should be done with the ContentDialogClosed method:

 

        protected override void ContentDialogClosed(object sender, System.EventArgs e)

        {

            Window window = sender as Window;

            if (window != null)

            {

                this.Region.Deactivate(window.Content);

                this.CloseContentDialog(window);

            }

        }    

 

You will also need to modify the CloseContentDialog method to receive the window to close:

 

        protected void CloseContentDialog(Window contentDialog)

        {

            if (contentDialog != null)

            {

                contentDialog.Closed -= this.ContentDialogClosed;

                contentDialog.Close();

                contentDialog.Content = null;

                contentDialog.Owner = null;

            }

        }     

 

Finally you should modify the RegisterNewPopupRegion method of the  RegionPopupBehaviors class to use a Region instead of a SingleActiveRegion. The code should look as follows (changed code in bold)

 

       public static void RegisterNewPopupRegion(DependencyObject owner, string regionName)

        {

            // Creates a new region and registers it in the default region manager.

            // Another option if you need the complete infrastructure with the default region behaviors

            // is to extend DelayedRegionCreationBehavior overriding the CreateRegion method and create an

            // instance of it that will be in charge of registering the Region once a RegionManager is

            // set as an attached property in the Visual Tree.

            IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

            if (regionManager != null)

            {

                IRegion region = new Region();

                DialogActivationBehavior behavior;

#if SILVERLIGHT

                behavior = new PopupDialogActivationBehavior();

#else

                behavior = new WindowDialogActivationBehavior();

#endif

                behavior.HostControl = owner;

 

                region.Behaviors.Add(DialogActivationBehavior.BehaviorKey, behavior);

                regionManager.Regions.Add(regionName, region);

            }

        }

 

Hope it helps!

 

Matias Bonaventura

http://blogs.southworks.net/matiasb

Jul 15, 2009 at 1:57 PM

Hi all,

Matias,

 

I tried your solution but it doesn't work, the mother "window" is always closed when a child is opened. Is there any fix to this ?

 

BR

Jul 16, 2009 at 10:48 PM

Hi Br, 

I repeated those steps and it worked for me. Its opening several popups and even new popups from within other popups.  

I uploaded a solution based on the HelloWorld Quickstart with this new beheavior so that you can compare with your scenario.
You can download the solution here (the code is "as is" , just show this behavior and by no means demonstrate good practices) : http://blogs.southworks.net/matiasb/files/2009/07/multiplepopupsdesktop.zip

 

Hope it helps! 

Matias Bonaventura
http://blogs.southworks.net/matiasb

Oct 4, 2011 at 10:14 PM

Hi Matias!

After two years your solution remains actual!

It was helpful for me, thank you!

Regards,

Mykola

Nov 18, 2011 at 6:01 AM
Edited Nov 18, 2011 at 7:51 AM

Great solution, it helped me a lot!

I've modified the solution further as two of the StockTrader unit tests fails

The unit test doesn't have a parent Window (using a MockWindow and does not get its parent set with the modification) in this line:

this.CloseContentDialog(((FrameworkElement)e.OldItems[0]).Parent as Window);

I investigated this further and came up with this solution (the commented lines refer to the above example)

In DialogActivationBehaviour.cs

            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                var viewContent = ((FrameworkElement)e.OldItems[0]);

                var contentWindow = FindContentWindow(viewContent);

                //this.CloseContentDialog(((FrameworkElement)e.OldItems[0]).Parent as Window);
                this.CloseContentDialog(contentWindow);
            }
        private void CloseContentDialog(IWindow contentWindow)
        {
            if (contentWindow != null)
            {
                contentWindow.Closed -= this.ContentDialogClosed;
                contentWindow.Close();
                contentWindow.Content = null;
                contentWindow.Owner = null;

                contentWindows.Remove(contentWindow);
            }
        }

        private void CloseContentDialog(Window window)
        {
            if (window != null)
            {
                window.Closed -= this.ContentDialogClosed;
                window.Close();
                window.Content = null;
                window.Owner = null;
            }
        }
        private void ContentDialogClosed(object sender, System.EventArgs e)
        {
            Window window = sender as Window;

            if (window != null)
            {
                this.Region.Deactivate(window.Content);
                this.CloseContentDialog(window);
            }

            IWindow contentWindow = sender as IWindow;

            if (contentWindow != null)
            {
                this.Region.Deactivate(contentWindow.Content);
                this.CloseContentDialog(contentWindow);
            }
        }
        private IWindow FindContent(object usercontrol)
        {
            return contentWindows.FirstOrDefault(w => w.Content == usercontrol);
        }

        private void PrepareContentDialog(object view)
        {
            IWindow window = this.CreateWindow();

            window = this.CreateWindow();
            window.Content = view;
            window.Owner = this.HostControl;
            window.Closed += this.ContentDialogClosed;
            window.Style = this.GetStyleForView();

            contentWindows.Add(window);

            window.Show();
        }

   private IList<IWindow> contentWindows = new List<IWindow>();