[英]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.