防止Prism创建新的导航视图
本文关键字:导航 视图 Prism 创建 防止 | 更新日期: 2023-09-27 18:09:28
我使用Prism 5.0,在配置它以重用现有视图时遇到了麻烦。无论何时调用IRegionManager.RequestNavigate(string regionName, Uri source)
,它都会创建一个新视图,而不是使用先前创建的视图。奇怪的是,CLRProfiler还指出Prism的区域管理器持有对所有先前创建的视图实例的引用,从而导致内存泄漏。
我的视图模型实现INavigationAware
,并在IsNavigationTarget()
中返回true
,但该方法从未被调用。我试着在视图上实现它,结果也一样。
作为测试,我在视图上实现了IActiveAware
,这表明只要我导航到另一个视图(我不确定这是否相关),它就会被停用。
我发现了这个问题:PRISM WPF - Navigation每次都会创建新的视图,但我的V-VM命名约定与答案匹配(顺便说一下,我使用AutoFac)。
我只找到了一个解决方法:在视图模型的INavigationAware.OnNavigatedFrom()
实现中使用NavigationContext.NavigationService.Region.Remove()
从区域中删除活动视图。当我这样做时,Prism的区域管理器将释放对视图的引用。这是有效的,但是在需要时总是重新创建视图似乎效率低下。
几乎所有关于SO的相关问题都是问如何在导航事件上创建新视图,所以我假设默认行为是重用视图。这里需要指针
编辑
我们使用AutoFac的AutofacExtensions.RegisterTypeForNavigation<T>(this ContainerBuilder builder, string name = null)
来注册视图。我们确实使用IRegionManager.RequestNavigate()
在视图之间导航。INavigationAware
是在ViewModels上实现的。然而,当INavigationAware.OnNavigatedTo()
和OnNavigatedFrom()
被调用时,IsNavigationTarget()
永远不会被调用(即使视图实现了INavigationAware
,后者也不会被调用)。
我可以通过在视图的元素中设置一个断点来检测创建了一个新视图。CLRProfiler堆转储还显示,区域管理器具有与导航到的视图实例数量相同的视图实例。ViewModels只被创建一次,因为它们作为单实例在AutoFac中注册。
作为临时措施,我们让视图实现IRegionMemberLifetime
,其中KeepAlive
返回false
。这不是很有效,因为每次需要视图时都要重新创建视图,但是这会阻止区域管理器保留以前的视图。
根据文档:
"对于Prism确定目标视图的类型,视图的名称在导航URI中应该与实际的目标类型相同短类型名称。例如,如果您的视图由类中指定的视图名称导航URI应该是EmployeeDetailsView。"https://prismlibrary.github.io/docs/wpf/Navigation.html
那么在你的模块中:
_container.RegisterTypeForNavigation<Views.MyView>(nameof(Views.MyView));
导航到这个视图:
Uri uri = new Uri(nameof(Views.MyView), UriKind.Relative);
this.RegionManager.RequestNavigate("ContentRegion", uri);
只要IRegionManager。调用RequestNavigate(string regionName, Uri源),它将创建一个新视图,而不是使用先前创建的视图
我不能复制这个行为。当使用导航框架时,默认情况下Prism会重用每个视图,而不管您是否使用INavigationAware接口。这意味着它将把视图保存在区域管理器中(不是内存泄漏,这是故意的)。如果您不希望重用视图,则需要使用IRegionMemberLifetime接口或属性,并告诉Prism不要重用视图。在这种情况下,视图将从区域管理器中删除,并将创建一个新视图。
我的视图模型实现了INavigationAware,并在IsNavigationTarget()中返回true,但该方法从未被调用。我试着在视图上实现它,结果也一样。
如果是这种情况,则说明您没有使用RequestNavigate。您可以使用IRegion手动向区域添加视图。添加,或者你的ViewModel没有被设置为DataContext。如果是这种情况,这些方法将不会被调用。
如何确定是否正在创建新视图?你是否在视图和视图模型的向量中设置了断点?您是否正确地注册了用于导航的视图?
我知道这是一个老问题。但我自己也遇到过这个问题,如果其他人也遇到过,那么很可能在某个时候再次出现。
我遇到的问题是我的页面名称。
为了使导航更容易,我创建了一个PageNames类,其内容如下:
public static class PageNames
{
public const string MyPage= "MyPageView";
}
,当注册页面和导航时,我的类有点像下面的
Kernel.RegisterTypeForNavigation<MyPageView>( PageNames.MyPage );
我的大多数页面有相同的名称作为他们的类,IE MyPageView = MyPageView。但是,有些页面缺少页面名称的View部分。
当PRISM检查一个页面是否存在于一个区域时,它执行以下操作:
protected virtual string GetContractFromNavigationContext(NavigationContext navigationContext)
{
if (navigationContext == null) throw new ArgumentNullException(nameof(navigationContext));
var candidateTargetContract = UriParsingHelper.GetAbsolutePath(navigationContext.Uri);
candidateTargetContract = candidateTargetContract.TrimStart('/');
return candidateTargetContract;
}
返回页面名称。MyPageView。然后,请求导航内容加载器调用此方法来检查页面是否存在于该区域内。
return region.Views.Where(v =>
string.Equals(v.GetType().Name, candidateNavigationContract,
StringComparison.Ordinal) ||
string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
,我认为(不确定如何直接调试PRISM)正在检查页面名称是否与类名称匹配,或者包含名称空间的全名。由于页名与类名不相同,因此无法找到它,并将该页的新实例添加到区域管理器中。
总而言之,最好的修复方法是确保pagename =类名