簡體   English   中英

防止棱鏡在導航上創建新視圖

[英]prevent Prism from creating new view on navigation

我使用Prism 5.0,但在配置它以重用現有視圖時遇到麻煩。 每當IRegionManager.RequestNavigate(string regionName, Uri source) ,它都會創建一個新視圖,而不是使用以前創建的視圖。 奇怪的是,CLRProfiler還指示Prism的區域管理器保留對所有先前創建的視圖實例的引用,從而導致內存泄漏。

我的視圖模型實現了INavigationAware ,並在IsNavigationTarget()返回true ,但從未調用該方法。 我也嘗試在視圖上實現相同的結果。

作為測試,我在視圖上實現了IActiveAware ,該視圖表明,一旦導航到另一個視圖,它就會被停用(我不確定這是否相關)。

我發現了這個問題: PRISM WPF-導航每次都會創建新視圖,但是我的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()從未調用過IsNavigationTarget() (即使視圖實現了INavigationAware也不會調用后者)。

我可以通過在視圖的ctor中設置斷點來檢測是否創建了新視圖。 CLRProfiler堆轉儲還顯示,區域管理器具有的視圖實例與被導航到的視圖一樣多。 ViewModel僅創建一次,因為它們已在AutoFac中注冊為單實例。

作為臨時措施,我們使Views實現IRegionMemberLifetime ,其中KeepAlive返回false 這不是很有效,因為每次需要時都會重新創建視圖,但這會阻止區域管理器保留先前的視圖。

每當調用IRegionManager.RequestNavigate(string regionName,Uri源)時,它都會創建一個新視圖,而不是使用以前創建的視圖

我無法復制此行為。 使用導航框架時,默認情況下,Prism會重用每個視圖,而不管是否使用INavigationAware接口。 這意味着它將視圖保留在區域管理器中(不是內存泄漏,這是有意完成的)。 如果您不想重用視圖,則需要使用IRegionMemberLifetime接口或屬性,並告訴Prism不要重用視圖。 在這種情況下,該視圖將從區域管理器中刪除,並創建一個新視圖。

我的視圖模型實現了INavigationAware,並在IsNavigationTarget()中返回true,但從未調用該方法。 我也嘗試在視圖上實現相同的結果。

在這種情況下,您不使用RequestNavigate。 您可能使用IRegion.Add將視圖手動添加到區域中,或者可能未將ViewModel設置為DataContext。 在這種情況下,將不會調用這些方法。

您如何確定是否正在創建新視圖? 您是否在Views和ViewModels的ctor中設置一個斷點? 您是否正確注冊了視圖以進行導航?

我知道這是一個老問題。 但是我本人只是遇到了這個問題,如果其他人遇到了這個問題,那么很可能會在一段時間后再次出現。

我遇到的問題是頁面名稱。

為了簡化導航,我創建了一個具有以下內容的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)正在檢查頁面名稱,匹配的類名稱還是包含名稱空間的全名。 由於頁面名稱與類名稱不同,因此無法找到該頁面,並將該頁面的新實例添加到區域管理器中。

因此,總而言之,最好的解決方法是確保您的頁面名稱=類名稱。

根據文檔:

“為了讓Prism確定目標視圖的類型,導航URI中的視圖名稱應與實際目標類型的短類型名稱相同。例如,如果您的視圖是由MyApp.Views.EmployeeDetailsView類實現的,導航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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM