2016-09-29 83 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名稱。