You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MVVM与Autofac实现登录后窗口间用户数据传递问题

问题:登录后跳转MainWindow时用户ID重置为0,如何保留用户信息?

问题背景

我正在开发首个正式MVVM应用,登录是用户操作的第一步,MainWindow的Frame中承载着登录页面。希望用户输入用户名和密码后,打开新窗口并展示用户ID与名称。当前已使用MainViewModel、UserAccountViewModel及Autofac进行依赖注入,相关代码如下:

MainViewModel 核心代码

public IEventAggregator _eventaggregator;
private Func<IUserAccountViewModel> _useraccountviewmodelCreator;
private IUserAccountViewModel _useraccountViewModel;

public MainViewModel(INavigationViewModel navigationveiwmodel , Func<IUserAccountViewModel> useraccountviewmodelCreator,IEventAggregator EventAggregator)
{
    _useraccountviewmodelCreator = useraccountviewmodelCreator;
    _eventaggregator = EventAggregator;
    _eventaggregator.GetEvent<OpenDetailViewEvent>().Subscribe(OnOpenDetailView);
    _eventaggregator.GetEvent<AfterUserSavedEvent>().Subscribe(AfterUserSaved);
    NavigationViewModel = navigationveiwmodel;
    CreateNewUserCommand = new DelegateCommand(OnCreateNewUserExecute);
    LogInCommand = new DelegateCommand(onLoginExecute);
    _eventaggregator.GetEvent<OpenDetailViewEvent>().Subscribe(AfterLoginExecute);
}

private async void onLoginExecute()
{
    UserAccountViewModel = _useraccountviewmodelCreator();
    await UserAccountViewModel.LoadAsyncByPassword(UserName, HashSomeText(Password));
}

private async void AfterLoginExecute(int? Userid)
{
    UserAccountViewModel = _useraccountviewmodelCreator();
    await UserAccountViewModel.LoadAsync(Userid);
    CheckID = Userid.Value;
}

public INavigationViewModel NavigationViewModel { get; set; }
public ICommand CreateNewUserCommand { get; set; }
public ICommand LogInCommand { get; set; }
public IUserAccountViewModel UserAccountViewModel 
{ 
    get { return _useraccountViewModel; } 
    set { _useraccountViewModel = value; OnPropertyChanged(); } 
}

UserAccountViewModel 核心代码

public async Task LoadAsyncByPassword(string UserName, string password)
{
    var user = await _userRepository.GetAsyncByPassword(UserName, password);
    if(user!=null)
    {
        _eventaggregator.GetEvent<OpenDetailViewEvent>().Publish(user.Id);
    }
}

Bootstrapper 注册代码

var bulider = new ContainerBuilder();
bulider.RegisterType<EventAggregator>().As<IEventAggregator>().SingleInstance();
bulider.RegisterType<BudGDbContext>().AsSelf();
bulider.RegisterType<SplashScreen>().AsSelf();
bulider.RegisterType<AuthorisationsWindow>().AsSelf();
bulider.RegisterType<MainWindow>().AsSelf();
bulider.RegisterType<MainViewModel>().AsSelf();
bulider.RegisterType<UserAccountViewModel>().As<IUserAccountViewModel>();
bulider.RegisterType<NavigationViewModel>().As<INavigationViewModel>();
bulider.RegisterType<LookupDataSeervice>().AsImplementedInterfaces();
bulider.RegisterType<UserRepository>().As<IUserRepository>();
return bulider.Build();

当前问题:登录后能获取到用户ID与名称,但切换至MainWindow后ID重置为0,请问如何从AuthorisationsWindow跳转至MainWindow并保留用户ID与名称?


解决方案

核心问题分析

你的问题根源在于窗口跳转时ViewModel实例是相互独立的,当前通过EventAggregator传递用户ID的方式只能在事件订阅时临时获取,但新打开的MainWindow对应的MainViewModel初始化时没有持久化这个状态,导致后续ID丢失。解决思路是引入一个全局的用户状态存储服务,让所有ViewModel能共享登录用户的信息。

1. 创建全局用户状态服务

首先定义一个单例服务来存储当前登录用户的信息,确保整个应用中只有一个实例:

public interface ICurrentUserService
{
    int? CurrentUserId { get; set; }
    string CurrentUserName { get; set; }
    // 可根据需求扩展更多用户属性,比如邮箱、角色等
}

public class CurrentUserService : ICurrentUserService
{
    public int? CurrentUserId { get; set; }
    public string CurrentUserName { get; set; }
}

然后在Bootstrapper中将其注册为单例:

// 添加这行代码到ContainerBuilder注册中
builder.RegisterType<CurrentUserService>().As<ICurrentUserService>().SingleInstance();

2. 修改登录逻辑,持久化用户信息到全局服务

在UserAccountViewModel中注入ICurrentUserService,登录成功后将用户信息存入该服务:

// 先注入服务
private readonly ICurrentUserService _currentUserService;

public UserAccountViewModel(IUserRepository userRepository, 
                           IEventAggregator eventAggregator,
                           ICurrentUserService currentUserService)
{
    _userRepository = userRepository;
    _eventaggregator = eventAggregator;
    _currentUserService = currentUserService;
}

public async Task LoadAsyncByPassword(string UserName, string password)
{
    var user = await _userRepository.GetAsyncByPassword(UserName, password);
    if(user!=null)
    {
        // 把用户信息存入全局服务
        _currentUserService.CurrentUserId = user.Id;
        _currentUserService.CurrentUserName = user.Name;
        _eventaggregator.GetEvent<OpenDetailViewEvent>().Publish(user.Id);
    }
}

3. 让MainViewModel初始化时读取全局用户状态

修改MainViewModel的构造函数,注入ICurrentUserService,并在初始化时直接读取已存储的用户信息:

private readonly ICurrentUserService _currentUserService;

public MainViewModel(INavigationViewModel navigationveiwmodel, 
                     Func<IUserAccountViewModel> useraccountviewmodelCreator,
                     IEventAggregator EventAggregator,
                     ICurrentUserService currentUserService)
{
    _useraccountviewmodelCreator = useraccountviewmodelCreator;
    _eventaggregator = EventAggregator;
    _currentUserService = currentUserService;
    
    _eventaggregator.GetEvent<OpenDetailViewEvent>().Subscribe(OnOpenDetailView);
    _eventaggregator.GetEvent<AfterUserSavedEvent>().Subscribe(AfterUserSaved);
    NavigationViewModel = navigationveiwmodel;
    CreateNewUserCommand = new DelegateCommand(OnCreateNewUserExecute);
    LogInCommand = new DelegateCommand(onLoginExecute);
    _eventaggregator.GetEvent<OpenDetailViewEvent>().Subscribe(AfterLoginExecute);

    // 初始化时直接读取全局服务中的用户ID
    if (_currentUserService.CurrentUserId.HasValue)
    {
        CheckID = _currentUserService.CurrentUserId.Value;
        // 可以直接加载用户信息,无需等待事件
        await AfterLoginExecute(_currentUserService.CurrentUserId);
    }
}

4. 优化窗口跳转的生命周期

确保登录成功后,关闭登录窗口(AuthorisationsWindow)并打开MainWindow时,MainWindow能正确获取全局状态。可以在AfterLoginExecute方法中添加窗口切换逻辑:

private async void AfterLoginExecute(int? Userid)
{
    UserAccountViewModel = _useraccountviewmodelCreator();
    await UserAccountViewModel.LoadAsync(Userid);
    CheckID = Userid.Value;

    // 关闭登录窗口,打开MainWindow
    var mainWindow = App.Container.Resolve<MainWindow>();
    mainWindow.Show();
    // 找到并关闭当前的登录窗口
    var loginWindow = Application.Current.Windows.OfType<AuthorisationsWindow>().FirstOrDefault();
    loginWindow?.Close();
}

注意:这里假设你在App类中保留了Autofac容器的引用(比如public static IContainer Container { get; set; }),这样可以直接解析MainWindow实例。

5. 验证ViewModel注册方式

确保MainViewModel是每个MainWindow实例对应一个ViewModel(默认的AsSelf()注册方式是正确的),不要将其注册为单例,否则会导致多窗口状态冲突。全局状态只通过ICurrentUserService这个单例来维护即可。


总结

通过引入全局单例的用户状态服务,我们将登录用户的信息持久化到一个全局可访问的位置,这样无论跳转到哪个窗口,对应的ViewModel都能读取到正确的用户信息,避免了ID重置的问题。这种方式也符合MVVM的解耦原则,把状态管理从ViewModel中分离出来,让代码更易于维护和扩展。

内容的提问来源于stack exchange,提问作者Mahmoud Samy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:56:18