前文
本篇文章主要討論在WPF當中使用Prism區域導航的失效的問題, 在其它的博客當中也出現了多次討論這個問題以及對應的解決方法,
例如重寫OnInitialized方法等等。我認為這都不是解決問題的根源, 既然如此, 下面我們將來分析Prism的IRegionManager的具體流程。
Prism初始化過程
首先, 我們分析一下下面的代碼, 用於創建應用程序的主頁
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
那么, 在這個MainWindow當中, 如果我們定於以下一個區域, 則可以使用IRegionManager進行導航操作, 如下所示:
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ShowRegion" />
現在問題就來了, 一旦我們把這個窗口的實例銷毀, 自己重新創建一個MainWindow的時候, 你會發現你無法進行導航了, 這個時候就是大家理解的那個問題, 是Region失效了?
並不是, 為了解決這個問題, 我們需要了解Prism框架本身做了什么動作, 查看源代碼之后, 發現以下初始化代碼:
var shell = CreateShell();
if (shell != null)
{
MvvmHelpers.AutowireViewModel(shell);
RegionManager.SetRegionManager(shell, _containerExtension.Resolve<IRegionManager>());
RegionManager.UpdateRegions();
InitializeShell(shell);
}
了解到, 從一開始獲取到MainWindow之后, 陸續進行了上下文綁定, 設置IRegionManager實例以及更新區域的操作。
在這里, 我們至少了解了幾個東西。
- MainWindow的DataContext初始化的時機
- MainWindow窗口當中IRegionManager的初始化過程
- 區域刷新的動作
完成了這些動作之后, 最終ShowDialog展示了首頁, 於是,我們可以在這里愉快的使用IRegionManager進行導航操作。
分析結果
如果想要實現在某個窗口當中進行導航, 除了定義區域之外, 你還需要做的就是給窗口設置IRegionManager的實例以及刷新區域, 核心就是這兩行代碼:
RegionManager.SetRegionManager(shell, _containerExtension.Resolve<IRegionManager>());
RegionManager.UpdateRegions();
既然我們了解到這個之后, 還有一個問題也順其自然的解決了, 在Prism當中使用彈窗服務不能導航的問題, 因為在Prism框架提供的IDialogService中並沒有實現
設置IRegionManager以及刷新區域, 這就是問題的根源, 所以我們必須手動的去修改實現達到支持導航的功能。
彈窗中實現導航
示例: 以下代碼, 展示了如何在彈窗當中設置區域以及刷新區域的問題。
var provider = ContainerLocator.Container.Resolve<IContainerProvider>();
var regionManager = ContainerLocator.Container.Resolve<IRegionManager>();
var win = provider.Resolve<object>("ShowWindow");
if (win is Window view)
{
RegionManager.SetRegionManager(view, regionManager);
RegionManager.UpdateRegions();
view.ShowDialog();
}
當然, 你完全可以自行實現IDialogService接口覆蓋Prism提供的內部實現, 以達到彈窗支持導航的行為, 例如:
public interface IMyDialogService : IDialogService
{
}
public class MyDialogService : DialogService, IMyDialogService
{
public MyDialogService(IContainerExtension containerExtension)
: base(containerExtension)
{
}
public new void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback)
{
//...
}
}
注意
如果在彈窗服務當中設置區域, 在窗口再次打開的同時會提示區域名稱已注冊的問題, 則你需要在關閉前移除原有的所有區域名即可。
regionManager.Regions.Remove("ShowRegion");
總結
對於任何窗口, 我們都可以使用IRegionManager進行導航操作, 在第一次框架初始化的時候, 只不過是Prism內部幫我們處理了區域的設置以及刷新行為。
而如何我們想在其它地方使用區域導航, 則需要手動設置區域以及刷新區域即可。