站长 15 年开始使用Prism 4,去年(2020 年 😊)也使用Prism 8做开源项目,今天分享处理Prism Region的一个问题。
問題描述
一般客戶端項目常規操作流程是:彈出登錄窗口=》帳號驗證成功=》關閉登錄窗口=》彈出主窗口=》在主窗口作業。
常規登錄流程

像上面的 gif 圖主窗體,左側是一棵樹,右側是 tabcontrol,使用 prism 模塊中注入視圖代碼:
public class ModuleOfLogModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainTabItemView, MainTabItemViewModel>(KEY_OF_CURRENT_MODULE);
}
}
主工程 tabcontrol 為模塊視圖顯示區域:
<TabControl
prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainTabRegion}"
/>
點擊左側菜單樹時,動態導航模塊視圖:
private void RaiseSelectedItemHandler(CustomMenuItem menuItem)
{
// 此处省略N多代码
region.RequestNavigate(menuItem.Key);
// 此处省略N多代码
}
實際運行時發現導航沒有起作用,原來的操作是登錄成功,直接 new 的主窗體彈出,app.xaml.cs 中註冊的登錄窗體視圖:
protected override Window CreateShell()
{
return Container.Resolve<LoginView>();
}
百度到也有人遇到這個問題:
- wpf prism 框架下先登錄窗體再打開主窗體(csdn 討論帖,原連結已不再維護)
討論區很火,沒看到想要的結果。
這篇給出的答案是手動再註冊區域管理器,站長沒有採用。
RegionManager.SetRegionName( theNameOfTheContentControlInsideThePopup, WellKnownRegionNames.DataFeedRegion );
RegionManager.SetRegionManager( theNameOfTheContentControlInsideThePopup, theRegionManagerInstanceFromUnity );
這個代碼是將登錄與主窗體作為用戶控制項,app.xaml.cs 中註冊 shellview,shellview 中設置一個區域,兩個用戶控制項通過導航在這個區域切換,效果是沒問題,主窗體內的區域能正常使用,但自定義的登錄界面和主界面,一般標題欄啥的都不一樣,這種做法比較麻煩,不推薦使用。
看问题 3 类似的描述:Prism MVVM 应用 登录后切换主窗体实现
应用场景
使用Prism7开发WPF程序,编码采用MVVM形式。当程序启动时,首先进入一个登录界面,进行登录认证,认证成功后转入程序布局主窗口。
设计思路
WPF程序框架搭建后,程序中存一个Shell.xaml,相当于表演者的唯一舞台。登录窗体(以下简称 LoginView)和程序布局主窗体(以下简称 MainView),分别利用IRegionManager进行管理,根据需要在不同时机相继出场表演。所有操作均由各自ViewModel(简称VM)代码完成。
1.当程序启动后,Shell通过VM,使用RegionManager的Add方法激活LoginView,(注:站长补充描述=登录验证成功,注销LoginView,再通过Add方法激活MainView)
站長採用的解決方案
baidu 基本上沒有找到比較合適的方案了,這個問題糾結了我幾天(每天晚上搞 2、3 個小時,站長平時工作已經不做 wpf 了)。
还好我有科学上网的方法,在 YouTube 上Adding a Prism Login Screen找到一个答案。

解决方案的代码很简单,在 app.xaml.cs 中添加如下代码,在初始化 shell 之前(InitializeShell,shell指CreateShell()注册的主窗体),先弹出登录窗口,验证成功再初始化 shell(base.InitializeShell(shell)):
protected override void InitializeShell(Window shell)
{
LoginView loginView = new LoginView();
if (loginView.ShowDialog() == true)
{
var shellVM = shell.DataContext as MainWindowViewModel;
shellVM.InitData();
base.InitializeShell(shell);
}
else
{
Application.Current.Shutdown(-1);
}
}
文末探討
其实该解决方案还是有问题的,在调用InitializeShell(Window shell)之前,站长调试发现模块视图已经执行了初始化,按道理说应该是登录成功后模块才执行初始化的,更多思考留给你,有什么建议欢迎Dotnet9网站留言。