2016-09-29 53 views
0

我使用Prism 5.0,并且在配置它以重用现有视图时遇到问题。每当调用IRegionManager.RequestNavigate(string regionName, Uri source)时,它将创建一个新视图,而不是使用先前创建的视图。奇怪的是,CLRProfiler还指出Prism的区域管理器持有引用所有先前创建的视图实例,导致内存泄漏。阻止棱镜创建导航新视图

我的视图模型实现了INavigationAware,并在IsNavigationTarget()中返回true,但该方法从未被调用过。我试图在视图上实现它,并得到相同的结果。

作为测试,我在视图上实现了IActiveAware,这表明一旦我导航到另一个视图(我不确定这是否相关),它就会停用。

我发现这个问题:PRISM WPF - Navigation creates new view every time但我的V-VM命名约定匹配那些答案(我使用AutoFac,顺便说一句)。

我只找到了解决方法:在视图模型的INavigationAware.OnNavigatedFrom()实现中使用NavigationContext.NavigationService.Region.Remove()从区域中删除活动视图。当我这样做时,Prism的区域经理会发布对该视图的引用。这有效,但在需要时总是重新创建视图似乎效率低下。

几乎所有关于SO的相关问题都要求如何在导航事件上创建新视图,所以我假设默认行为是视图被重用。我需要指针。

编辑

我们使用AutoFac的AutofacExtensions.RegisterTypeForNavigation<T>(this ContainerBuilder builder, string name = null)注册的意见。我们确实使用IRegionManager.RequestNavigate()在视图之间导航。在ViewModels上实现了INavigationAware。但是,虽然调用了INavigationAware.OnNavigatedTo()OnNavigatedFrom(),但从未调用IsNavigationTarget()(即使视图实现了INavigationAware,也不调用后者)。

我可以通过在视图的ctor中设置断点来检测到新视图。 CLRProfiler堆转储还显示区域管理器拥有多次导航到的视图的实例。 ViewModels只创建一次,因为它们是以AutoFac作为单一实例注册的。

作为一项临时措施,我们制定了意见实施IRegionMemberLifetime,其中KeepAlive返回false。这不是非常有效,因为每次需要时都会重新创建视图,但它会阻止区域经理坚持以前的视图。

+0

您需要使用IRegionMemberLifetime和KeepAlive属性。 –

+0

@AyyappanSubramanian如果视图实现IRegionMemberLifetime,并且KeepAlive返回true,则区域管理器会保留对视图的引用,但会在导航到该视图时创建一个新视图(如果该视图未实现IRML,则该视图必须是默认视图)。如果KeepAlive返回false,则该视图将被丢弃,因此需要重新创建。我的问题是如何让Prism重用原始视图,所以这不是一个解决方案。 – Drew

+0

我目前有同样的问题,虽然不是我所有的意见。它似乎是任何低于特定页面,这是导致我的问题。 – user3265613

回答

0

每当IRegionManager.RequestNavigate(字符串regionName,开放的源)被调用时,它会创建的,而不是使用以前创建

我不能重复这种行为的看法的新观点。在使用导航框架时,无论您使用的是INavigationAware接口,默认情况下Prism都会重用每个视图。这意味着它将保持区域管理器中的视图(不是内存泄漏,这是有意的)。如果您不希望重用视图,则需要使用IRegionMemberLifetime接口或属性,并告诉Prism不要重用视图。在这种情况下,视图将从区域管理器中删除,并创建一个新视图。

我的视图模型实现INavigationAware,并在IsNavigationTarget()中返回true,但该方法从不调用。我试图在视图上实现它,并得到相同的结果。

如果是这种情况,那么您并未使用RequestNavigate。您可能正在使用IRegion.Add手动添加视图到区域,或者您的ViewModel未被设置为DataContext。如果是这种情况,则不会调用这些方法。

您如何确定是否正在创建新视图?你在Views和ViewModels的ctor中设置了一个断点吗?您是否正确地注册了您的导航视图?

+0

感谢您的回答。我编辑了这篇文章来澄清一些事情。 – Drew

+0

我讨厌复活老问题,但我有完全相同的问题。但它只影响某些观点。有没有办法在运行我的主应用程序时调试棱镜源? – user3265613

+0

@ user3265613我建议你从Prismlibrary Github页面抓取源代码,并引用项目文件而不是NuGet包。 https://github.com/PrismLibrary/Prism/tree/master/Source/Wpf – Gusdor

0

我知道这是一个老问题。但我自己也遇到过这个问题,如果其他人有这个问题,那么它有可能会再次回来。

这个问题,因为我有它,是我的网页名称。

为了让导航更容易,我创建了一个PageNames类,内容如下。

public static class PageNames 
{ 
    public const string MyPage= "MyPageView"; 
} 

和注册页面和导航时我的课是有点像以下

Kernel.RegisterTypeForNavigation<MyPageView>(PageNames.MyPage); 

我的大多数网页有相同的名称作为自己的阶级,IE MyPageView = MyPageView。但是有些页面缺少页面名称的视图部分。

当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 = class名称。