Re-navigate to a particular view in multi-view region

349 views Asked by At

I'm working on a WPF application that's utilizing the Microsoft Prism framework. One aspect of the application is a "modal" region that can hold any number of modal windows that overlays over the entire window. As more views are navigated into the region, each window slides to the right to allow the new window to occupy the center of the screen. Here's a more visual explanation:

When the "modal" region contains a single view:

When the "modal" region contains a single view

When another view is added to the region:

When another view is added to the region

When several more views are added:

When several more views are added

I have this working using a custom control that manages the animation and display of its children. Here's what the control's custom RegionAdapter's Adapt method looks like:

protected override void Adapt(IRegion region, ModalContainer regionTarget)
{
    region.ActiveViews.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler((o, e) =>
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            foreach(FrameworkElement element in e.NewItems)
            {
                regionTarget.AddChild(element);
            }
        }
        else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
        {
            foreach (FrameworkElement element in e.OldItems)
            {
                regionTarget.RemoveChild(element);
            }
        }
    });
}

My question is this: What's the best way to navigate back to an earlier window? Right now, the only way I know to trigger the RemoveChild method above is to explicitly remove the view from the region, which requires that I keep a list of all the views currently in the region somewhere:

// to remove the most recently added view from the region
_regionManager.Regions["ModalRegion"].Remove(addedViews.Pop());

Ideally, I would be able to navigate backwards using Prism's "journaling" concept, but I don't see a way in my RegionAdapter to respond to when a view already in the region is re-navigated to.

Any hints would be much appreciated.

EDIT

I was able to achieve this functionality by following a suggestion by GOstrowsky (see the comments in the accepted answer) - I changed my region adapter to only maintain a single active view in the region (the view currently in the center of the screen). I then can target that view for removal via myRegion.ActiveViews.FirstOrDefault().

YET ANOTHER EDIT

I have since changed this implementation yet again, as we needed the ability to remove any of the views currently in the region, not just the last one. See the accepted answer for details.

2

There are 2 answers

0
Nathan Friend On BEST ANSWER

Initially, I solved this problem by only allowing a single region to be active, which guaranteed that removing the region's currently active view always removed the view currently in the center of the screen. However, since then, we've needed the ability to remove any of the views from the region, not just the first. To accomplish this, I realized that the Region.Views property can be cast to a List and then accessed by index:

List<object> allViews = modalRegion.Views.ToList<object>();

I'm a little uncomfortable with this solution, since the IViewsCollection definition inherits from IEnumerable, not IList; technically I could be handed a custom IViewsCollection that cannot be cast to an IList.... but in the short term I'm going to run with this.

3
GOstrowsky On

If you would ocassionally want to Navigate back to a previous View, you should not remove it from the Region when Navigating from it.

Instead, you could just deactivate it from the OnNavigatedFrom() method. And then, use the NavigationJournal to navigate back.

Regarding your RegionAdapter control you could modify it so that it could handle View activation and de-activation. For example, you could Publish a De/ActivationChanged event from each ViewModel OnNavigatedFrom() and OnNavigatedTo(), and handle these events on your custom control by Subscribing to it and performing the corresponding task to each event.

You can find more information about Navigation and Event Aggregation in the following MSDN Prism Guide chapters:

I hope this helps, Regards.