如何在ViewModel间访问属性?MVVM+Unity视图导航求助
解决WPF MVVM+Unity的用户控件导航问题
看起来你已经搭好了基础框架,现在核心问题集中在跨ViewModel的导航逻辑和接口定义不完整上,我来一步步帮你搞定:
1. 完善IMainViewModel接口
你当前的IMainViewModel是空接口,这导致LoginViewModel虽然注入了它,却没法访问ShowControl属性。我们需要把导航相关的属性暴露到接口里:
public interface IMainViewModel { UserControl ShowControl { get; set; } }
这样MainWindowViewModel实现接口时,ShowControl就会被子ViewModel识别并调用。
2. 把View注册到Unity容器
你现在只注册了主ViewModel,但LoginView和GameView还没加入容器,Unity没法解析它们。修改App.xaml.cs的ConfigureContainer方法:
private void ConfigureContainer() { _container = new UnityContainer(); // 注册主ViewModel为单例,确保全局唯一 _container.RegisterType<IMainViewModel, MainWindowViewModel>(new ContainerControlledLifetimeManager()); // 注册两个View,用相同生命周期管理器实现实例复用 _container.RegisterType<LoginView>(new ContainerControlledLifetimeManager()); _container.RegisterType<GameView>(new ContainerControlledLifetimeManager()); }
这样每次Resolve都会返回同一个实例,完全符合你不想重复创建对象的需求。
3. 修复LoginViewModel的导航逻辑
先修正命令名称的拼写错误(Navigae→Navigate),然后在execute方法里正确更新主ViewModel的ShowControl:
public class LoginViewModel : Bindable { private IMainViewModel _mainViewModel; public ICommand Navigate { get; set; } // 修正拼写 public LoginViewModel(IMainViewModel mainViewModel) { _mainViewModel = mainViewModel; Navigate = new RelayCommand(ExecuteNavigate, CanExecute); Display = "login view"; MyData = "Gopi "; } private bool CanExecute(object arg) => true; private void ExecuteNavigate(object obj) { // 从Unity容器获取GameView实例并切换 _mainViewModel.ShowControl = App.Container.Resolve<GameView>(); } // 修复MyData的SetProperty调用,确保双向绑定能更新UI private string _myData; public string MyData { get => _myData; set => SetProperty(ref _myData, value); } private string _display; public string Display { get => _display; set => SetProperty(ref _display, value); } }
4. 实现GameViewModel返回Login的逻辑
和LoginViewModel逻辑一致,在GameViewModel里注入IMainViewModel,添加返回命令:
public class GameViewModel : Bindable { private IMainViewModel _mainViewModel; public ICommand NavigateBack { get; set; } public GameViewModel(IMainViewModel mainViewModel) { _mainViewModel = mainViewModel; NavigateBack = new RelayCommand(ExecuteNavigateBack, CanExecute); Display = "game view"; } private bool CanExecute(object arg) => true; private void ExecuteNavigateBack(object obj) { // 切回已存在的LoginView实例 _mainViewModel.ShowControl = App.Container.Resolve<LoginView>(); } private string _display; public string Display { get => _display; set => SetProperty(ref _display, value); } }
对应的GameView里添加返回按钮:
<Button Grid.Row="1" Content="Back to Login View" Command="{Binding NavigateBack}" />
5. 优化MainViewModel的初始化逻辑
你的ShowControl getter初始化可以简化,直接借助Unity的实例管理:
class MainWindowViewModel : Bindable, IMainViewModel { private UserControl _showControl; public UserControl ShowControl { get { if (_showControl == null) _showControl = App.Container.Resolve<LoginView>(); return _showControl; } set => SetProperty(ref _showControl, value); } }
这样主窗口启动时会自动加载LoginView,完全符合你的初始需求。
验证效果
现在运行程序:
- 主窗口初始显示LoginView
- 点击"Navigate to Game View"按钮,切换到GameView
- 点击GameView里的"Back to Login View"按钮,切回LoginView
- 两个View的实例都是复用的,不会每次点击都创建新对象
内容的提问来源于stack exchange,提问作者Chandu
相关产品推荐
相关产品推荐

