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

运行MVVM时EventHandler为空问题及WPF共享变量实现求助

解决WPF MVVM中EventHandler为空的问题

我之前做WPF全局状态管理时也踩过这个坑,看你贴的代码片段,大概率是每次获取ViewModel时都new了新实例,再加上可能ViewModelController本身不是全局单例,导致事件订阅和触发的上下文对不上,EventHandler自然就空了。下面给你一步步的解决思路:

1. 修复ViewModel的实例创建方式

你当前的MenuViewModel和GamePageViewModel都是在get访问器里new新对象,这意味着每次调用controller.MenuViewModel都会得到一个全新的实例——之前订阅事件的是旧实例,新实例的事件根本没被订阅,触发的时候当然是空的。

改成在ViewModelController的构造函数里初始化一次,后续get返回同一个实例:

public class ViewModelController {
    private DataRowView _selectedUniverse;
    // 用私有字段存储ViewModel实例,确保全局唯一
    private readonly MenuViewModel _menuViewModel;
    private readonly GamePageViewModel _gamePageViewModel;
    private readonly PlayersViewModel _playersViewModel;

    // 事件声明
    public event EventHandler SelectedUniverseChanged;

    public ViewModelController() {
        // 构造函数里只初始化一次ViewModel
        _menuViewModel = new MenuViewModel(this);
        _gamePageViewModel = new GamePageViewModel(this);
        _playersViewModel = new PlayersViewModel(this);
    }

    // 返回已初始化的实例
    public MenuViewModel MenuViewModel => _menuViewModel;
    public GamePageViewModel GamePageViewModel => _gamePageViewModel;
    public PlayersViewModel PlayersViewModel => _playersViewModel;

    public DataRowView SelectedUniverse {
        get => _selectedUniverse;
        set {
            if (_selectedUniverse != value) {
                _selectedUniverse = value;
                // 触发事件前用?.Invoke避免空引用
                SelectedUniverseChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }
}

2. 确保事件订阅的正确性

在ViewModel的构造函数里订阅Controller的事件,并且要保证订阅的是同一个Controller实例。另外记得在ViewModel销毁时取消订阅,避免内存泄漏:

public class MenuViewModel {
    private readonly ViewModelController _controller;

    public MenuViewModel(ViewModelController controller) {
        _controller = controller;
        // 订阅Controller的事件
        _controller.SelectedUniverseChanged += OnSelectedUniverseChanged;
    }

    private void OnSelectedUniverseChanged(object sender, EventArgs e) {
        // 这里写事件触发后的逻辑,比如更新当前ViewModel的属性
        // 例如:UniverseName = _controller.SelectedUniverse["Name"].ToString();
    }

    // 提供清理方法,在View关闭时调用
    public void Cleanup() {
        _controller.SelectedUniverseChanged -= OnSelectedUniverseChanged;
    }
}

3. 保证ViewModelController是全局单例

整个应用里必须只有一个ViewModelController实例,否则不同ViewModel拿到的Controller不是同一个,事件自然无法互通。可以在App.xaml.cs里初始化单例:

public partial class App : Application {
    // 全局唯一的Controller实例
    public static ViewModelController GlobalController { get; private set; }

    protected override void OnStartup(StartupEventArgs e) {
        base.OnStartup(e);
        GlobalController = new ViewModelController();
        
        // 给主窗口设置DataContext
        var mainWindow = new MainWindow {
            DataContext = GlobalController.MenuViewModel
        };
        mainWindow.Show();
    }
}

最后验证

做完上面三步后,你可以在触发事件的地方(比如设置SelectedUniverse的时候)打个断点,检查SelectedUniverseChanged是否有订阅者——如果有,说明问题解决了;如果还是空,那要检查:

  • 是否所有ViewModel都用的是同一个GlobalController实例
  • 订阅事件的代码是否执行到了(比如ViewModel的构造函数有没有被调用)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:40:10