I encountered an interesting situation in my MEF application. The main workspace is a region registered on a ContentControl with only one active view at a time. Setup and navigation is working just fine. Now what I observed is that when I'm changing the view on this MainRegion all my bindings to the viewmodel are evaluated again.
To check this, I added a counter on my view model to see how often it is loaded. When I'm changing View A -> View B -> View A, then the counter will be 3:
I'm using the normal call to activate a region:
region.Activate(view);
When debugging this issue I saw that when activating a region, the old one gets deactivated, eventually setting the
ContentControl.Content = null;
This seems to modify the visual tree and reevaluates all bindings on the old view.
It seems to be a mixture of a MEF and WPF problem. Is there any way to prevent the evaluation of bindings when activating a new region or on the WPF side prevent re-evaluation of bindings when ContentControl.Content becomes null?
I found a similar question but without an answer here: WPF: disable bindings update on detach
This issue seems to be a problem of the ContentControl itself, see this post for further information: http://blogs.microsoft.co.il/tomershamam/2009/09/11/wpf-performance-sweets-contentcontrolcontent-null/
To avoid performance issues while switching between views in a regions you can keep the view in the visual tree by using another control for the region. Replace the ContentControl
by a modified ItemsControl
. I find a solution in this post: https://vslepakov.blogspot.de/2014/09/navigate-faster-with-prism-and-wpf.html. It works with hiding old views and show new views.
I modified this example like this:
public class RegionItemsControl : ItemsControl
{
protected override bool IsItemItsOwnContainerOverride(object item)
{
return false;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
((ContentPresenter)element).ContentTemplate = ItemTemplate;
}
}
Add it to your shell and mark it as region:
<controls:RegionItemsControl
prism:RegionManager.RegionName="MainRegion">
<controls:RegionItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</controls:RegionItemsControl.ItemsPanel>
<controls:RegionItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</controls:RegionItemsControl.ItemTemplate>
</controls:RegionItemsControl>
The navigation is handled by an event, which triggers the following code:
private FrameworkElement _lastView = null;
private bool LoadAndActivateWorkspaceAreaView(Type requestedViewType, IRegion region)
{
var viewToActivate = region.Views.FirstOrDefault(viewItem => viewItem.GetType() == requestedViewType) as FrameworkElement;
if (viewToActivate == null)
{
viewToActivate = MefContainer.GetExportedValue(requestedViewType) as FrameworkElement;
if (viewToActivate == null)
throw new InvalidOperationException("view not found!");
viewToActivate.Visibility = Visibility.Collapsed;
region.Add(viewToActivate); // Adds new view to RegionItemsControl
}
if (_lastView != null)
_lastView.Visibility = Visibility.Collapsed;
_lastView = viewToActivate;
_lastView.Visibility = Visibility.Visible;
}
One issue of this solution is, that bindings of "old" views which are stored in RegionItemsControl
are re-evaluated once on adding a new view to the region. This seems to be an issue of the ItemsControl
I think.