WPF MVVM模式下登录后导航至MainWindow视图不显示问题
我是WPF与MVVM模式的新手,尝试创建LoginWindow和包含HomeView等UserControl的MainWindow。以下是我的实现代码,目前遇到登录成功后导航到MainViewModel,但MainWindow视图未切换显示的问题。
1. App.xaml.cs 服务初始化代码
public App() { IServiceCollection services = new ServiceCollection(); services.AddSingleton(Provider => new LoginPage { DataContext = Provider.GetRequiredService<LoginViewModel>() }); services.AddSingleton<MainViewModel>(); services.AddSingleton<HomeViewModel>(); services.AddSingleton<LoginViewModel>(); services.AddSingleton<INavigationService, NavigationServices>(); services.AddSingleton<Func<Type, ViewModel>>(serviceProvider => viewModelType => (ViewModel)serviceProvider.GetRequiredService(viewModelType)); serviceProvider = services.BuildServiceProvider(); } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); if(serviceProvider != null) { var loginPage = serviceProvider.GetRequiredService<LoginPage>(); loginPage.Show(); } else { Debug.WriteLine("Service Provider is null"); } }
2. App.xaml 资源配置
<Application x:Class="NewHRD.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:NewHRD" xmlns:viewModel="clr-namespace:NewHRD.MVVM.ViewModels" xmlns:view="clr-namespace:NewHRD.MVVM.Views"> <Application.Resources> <DataTemplate DataType="{x:Type TypeName=viewModel:MainViewModel}"> <view:MainWindow/> </DataTemplate> <DataTemplate DataType="{x:Type TypeName=viewModel:HomeViewModel}"> <view:HomeView/> </DataTemplate> </Application.Resources> </Application>
3. LoginViewModel 登录命令代码
private async void ExecuteLoginCommand(object arg) { string valid = await APIUser.AuthenticateUser("user/Login", Username, Password); if (valid != null) { JObject jsonObject = JObject.Parse(valid); // Extract the "data" part from the JObject JToken dataToken = jsonObject["data"]; Dictionary<string, object> data = dataToken.ToObject<Dictionary<string, object>>(); DynamicModel dynamicModel = new DynamicModel(data); var principal = new GenericPrincipal( new GenericIdentity(Username), new string[] { data["access_rights_name"].ToString() }); Thread.CurrentPrincipal = principal; AppDomain.CurrentDomain.SetThreadPrincipal(principal); if (MessageBox.Show($"Masuk sebagai {Username} ?", "Logged In", MessageBoxButton.OKCancel, MessageBoxImage.Information) == MessageBoxResult.OK) { IsHomeButtonVisible = "Visible"; Window curWind = System.Windows.Application.Current.MainWindow; Navigation.NavigateTo<MainViewModel>(); } } else { Debug.WriteLine("Hmmm..."); } }
4. 导航服务实现代码
public interface INavigationService { ViewModel CurrentView { get; } void NavigateTo<T>() where T : ViewModel; } internal class NavigationServices : ObservableObject, INavigationService { private readonly Func<Type, ViewModel> _viewModelFactory; private ViewModel _currentView; public ViewModel CurrentView { get => _currentView; set { _currentView = value; OnPropertyChanged(); } } public NavigationServices(Func<Type, ViewModel> viewModelFactory) { _viewModelFactory = viewModelFactory; } public void NavigateTo<TViewModel>() where TViewModel : ViewModel { ViewModel viewModel = _viewModelFactory.Invoke(typeof(TViewModel)); CurrentView = viewModel; } }
问题现象
调试时确认已进入MainViewModel构造逻辑,但MainWindow视图并未显示,登录窗口依然存在,视图未完成切换。
问题分析与解决方案
核心问题
DataTemplate 误用:你在App.xaml中给
MainViewModel绑定了MainWindow的DataTemplate,但MainWindow是顶级窗口(Window类型),不是可嵌入的UI元素(如UserControl)。WPF的DataTemplate用于将ViewModel渲染为可视化元素,但Window不能作为DataTemplate的根元素被动态加载,这种绑定方式无效。导航逻辑未处理窗口切换:当前的NavigationService只是更新了
CurrentView的ViewModel,但没有处理窗口的关闭与新窗口的打开逻辑——你需要在登录成功后关闭LoginPage,然后实例化并显示MainWindow。
修复步骤
步骤1:修正App.xaml中的DataTemplate配置
将MainViewModel对应的DataTemplate改为针对MainWindow内部的内容区域(比如MainWindow里有一个ContentControl用于显示HomeView等),而不是绑定整个Window。如果MainWindow是独立的顶级窗口,直接移除这个DataTemplate:
<Application.Resources> <!-- 只保留UserControl类型视图的DataTemplate --> <DataTemplate DataType="{x:Type viewModel:HomeViewModel}"> <view:HomeView/> </DataTemplate> </Application.Resources>
步骤2:调整登录后的窗口切换逻辑
因为MainWindow是独立窗口,不需要通过DataTemplate导航,直接在登录成功后关闭当前LoginPage并打开MainWindow:
修改ExecuteLoginCommand中的逻辑:
if (MessageBox.Show($"Masuk sebagai {Username} ?", "Logged In", MessageBoxButton.OKCancel, MessageBoxImage.Information) == MessageBoxResult.OK) { IsHomeButtonVisible = "Visible"; // 关闭当前登录窗口 var loginWindow = Application.Current.Windows.OfType<LoginPage>().FirstOrDefault(); loginWindow?.Close(); // 从服务容器获取MainWindow并显示 var mainWindow = App.Current.serviceProvider.GetRequiredService<MainWindow>(); mainWindow.Show(); }
步骤3:在服务容器中注册MainWindow
在App.xaml.cs的服务注册代码中添加MainWindow的注册:
services.AddSingleton(Provider => new MainWindow { DataContext = Provider.GetRequiredService<MainViewModel>() });
步骤4:MainWindow内部视图导航配置(可选)
如果MainWindow内部需要切换HomeView等UserControl,确保MainWindow的XAML中有绑定到MainViewModel中视图属性的ContentControl:
<!-- MainWindow.xaml --> <Grid> <ContentControl Content="{Binding CurrentView}" /> </Grid>
然后在MainViewModel中注入INavigationService,维护内部视图切换逻辑:
public class MainViewModel : ViewModel { private readonly INavigationService _navigationService; public ViewModel CurrentView => _navigationService.CurrentView; public MainViewModel(INavigationService navigationService) { _navigationService = navigationService; // 初始化时导航到HomeView _navigationService.NavigateTo<HomeViewModel>(); } }
这样就能同时实现登录窗口到主窗口的切换,以及主窗口内部的视图导航。
内容的提问来源于stack exchange,提问作者Qu Da

