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 another view is added to the region:
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.
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 aList
and then accessed by index:I'm a little uncomfortable with this solution, since the
IViewsCollection
definition inherits fromIEnumerable
, notIList
; technically I could be handed a customIViewsCollection
that cannot be cast to anIList
.... but in the short term I'm going to run with this.