运行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
相关产品推荐
相关产品推荐

