I posted similar question few months ago Working with nested views using Prism with IsNavigationTarget which can return false, I\'m still not sure what is the proper way to do i
This is quite a troublesome problem. I recommend videos from Brian Lagunas himself where he provides a solution and explanation. For example this one. https://app.pluralsight.com/library/courses/prism-problems-solutions/table-of-contents
If you can watch it. If not I will try to explain.
The problem I believe is that IRegionManager
from the container is a singleton and whenever you use it it is the same instance, so when you are trying to inject a region in an already injected region it will not work and you need to have a separate RegionManager
for nested views.
This should fix it. Create two interfaces
public interface ICreateRegionManagerScope
{
bool CreateRegionManagerScope { get; }
}
public interface IRegionManagerAware
{
IRegionManager RegionManager { get; set; }
}
Create a RegionManagerAwareBehaviour
public class RegionManagerAwareBehaviour : RegionBehavior
{
public const string BehaviorKey = "RegionManagerAwareBehavior";
protected override void OnAttach()
{
Region.Views.CollectionChanged += Views_CollectionChanged;
}
void Views_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
IRegionManager regionManager = Region.RegionManager;
// If the view was created with a scoped region manager, the behavior uses that region manager instead.
if (item is FrameworkElement element)
{
if (element.GetValue(RegionManager.RegionManagerProperty) is IRegionManager scopedRegionManager)
{
regionManager = scopedRegionManager;
}
}
InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = regionManager);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = null);
}
}
}
private static void InvokeOnRegionManagerAwareElement(object item, Action invocation)
{
if (item is IRegionManagerAware regionManagerAwareItem)
{
invocation(regionManagerAwareItem);
}
if (item is FrameworkElement frameworkElement)
{
if (frameworkElement.DataContext is IRegionManagerAware regionManagerAwareDataContext)
{
// If a view doesn't have a data context (view model) it will inherit the data context from the parent view.
// The following check is done to avoid setting the RegionManager property in the view model of the parent view by mistake.
if (frameworkElement.Parent is FrameworkElement frameworkElementParent)
{
if (frameworkElementParent.DataContext is IRegionManagerAware regionManagerAwareDataContextParent)
{
if (regionManagerAwareDataContext == regionManagerAwareDataContextParent)
{
// If all of the previous conditions are true, it means that this view doesn't have a view model
// and is using the view model of its visual parent.
return;
}
}
}
invocation(regionManagerAwareDataContext);
}
}
}
}
Create ScopedRegionNavigationContentLoader
public class ScopedRegionNavigationContentLoader : IRegionNavigationContentLoader
{
private readonly IServiceLocator serviceLocator;
///
/// Initializes a new instance of the class with a service locator.
///
/// The service locator.
public ScopedRegionNavigationContentLoader(IServiceLocator serviceLocator)
{
this.serviceLocator = serviceLocator;
}
///
/// Gets the view to which the navigation request represented by applies.
///
/// The region.
/// The context representing the navigation request.
///
/// The view to be the target of the navigation request.
///
///
/// If none of the views in the region can be the target of the navigation request, a new view
/// is created and added to the region.
///
/// when a new view cannot be created for the navigation request.
public object LoadContent(IRegion region, NavigationContext navigationContext)
{
if (region == null) throw new ArgumentNullException("region");
if (navigationContext == null) throw new ArgumentNullException("navigationContext");
string candidateTargetContract = this.GetContractFromNavigationContext(navigationContext);
var candidates = this.GetCandidatesFromRegion(region, candidateTargetContract);
var acceptingCandidates =
candidates.Where(
v =>
{
var navigationAware = v as INavigationAware;
if (navigationAware != null && !navigationAware.IsNavigationTarget(navigationContext))
{
return false;
}
var frameworkElement = v as FrameworkElement;
if (frameworkElement == null)
{
return true;
}
navigationAware = frameworkElement.DataContext as INavigationAware;
return navigationAware == null || navigationAware.IsNavigationTarget(navigationContext);
});
var view = acceptingCandidates.FirstOrDefault();
if (view != null)
{
return view;
}
view = this.CreateNewRegionItem(candidateTargetContract);
region.Add(view, null, CreateRegionManagerScope(view));
return view;
}
private bool CreateRegionManagerScope(object view)
{
bool createRegionManagerScope = false;
if (view is ICreateRegionManagerScope viewHasScopedRegions)
createRegionManagerScope = viewHasScopedRegions.CreateRegionManagerScope;
return createRegionManagerScope;
}
///
/// Provides a new item for the region based on the supplied candidate target contract name.
///
/// The target contract to build.
/// An instance of an item to put into the .
protected virtual object CreateNewRegionItem(string candidateTargetContract)
{
object newRegionItem;
try
{
newRegionItem = this.serviceLocator.GetInstance
In your App.xaml
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton();
}
protected override void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors)
{
base.ConfigureDefaultRegionBehaviors(regionBehaviors);
regionBehaviors.AddIfMissing(RegionManagerAwareBehaviour.BehaviorKey, typeof(RegionManagerAwareBehaviour));
}
Coming to the finish.
Now in your ViewModelB
implement IRegionManagerAware
and have it as a normal property
public IRegionManager RegionManager { get; set; }
Then at your ViewB
implement ICreateRegionManagerScope
and have it as a get property
public bool CreateRegionManagerScope => true;
Now it should work.
Again I truly recommend the videos at Pluralsight from Brian on Prism. He has a couple of videos that help a lot when you are starting with a Prism.