这将是一个冗长的问题,但我长期以来一直通过构建丑陋的大型一体化类来避免这个问题。
例如,我正在编写一个采用 MVVM 设计的 WPF 独立应用程序(我也在使用 Caliburn.Micro),并且有一个带有 MainView
的 MainViewModel
。此 View 包含一个 StackPanel
,此 StackPanel
的内容绑定(bind)到一个 ViewModel CentralVM
:
<StackPanel DockPanel.Dock="Top">
<ContentControl Margin="10" Name="CentralVM"/>
</StackPanel>
在 MainViewModel
类中,我有几个其他的 ViewModel,
private PropertyChangedBase _centralVM = new PropertyChangedBase();
private LoggedInViewModel _loggedInVM = new LoggedInViewModel();
private LoginViewModel _logInVM = new LoginViewModel();
public PropertyChangedBase CentralVM {
get { return _centralVM; }
set { _centralVM = value; NotifyOfPropertyChange(() => CentralVM); }
}
public LoggedInViewModel LoggedInVM {
get { return _loggedInVM; }
private set { _loggedInVM = value; }
}
public LoginViewModel LoginVM {
get { return _logInVM; }
private set { _loginVM = value;}
}
现在,在我设置的 MainViewModel
的构造函数中
CentralVM = LoginVM
然后 StackPanel
自动绑定(bind)到 View LoginView
。 LoginView
会做您猜想的事情,即您可以输入(用户名、密码),并且有一个评估条目的按钮,如果它是正确的,我想切换设置 CentralVM
到 LoggedInVM
。但是按钮事件“存在于”LoginViewModel
的 LoginVM
实例中,那么如何访问 MainViewModel 中的属性
?CentralVM
这当然只是一般问题的一个例子。我的第一个想法是执行以下操作:
-LoginVM
包含一个名为 LoggedInAs
的属性(字符串类型),该属性在单击按钮时设置。
- 我向 MainViewModel
添加了一个方法,如下所示:
private _loggedIn = false;
private void CheckForLoginChange() {
if (_loggedIn == false && !String.IsNullOrEmpty(LoginVM.LoggedInAs)) {
_loggedIn = true;
CentralVM = LoggedInVM;
}
}
-最后,我将此方法调用添加到 LoginVM
的 setter,即
public LoginViewModel LoginVM {
get { return _logInVM; }
private set { _logInVM = value; CheckForLoginChange(); }
}
但这行不通。是不是因为当点击按钮事件时 LoginVM
改变了,但是没有调用 setter?
感谢在这方面的任何帮助。我非常感谢详细的回答,而不仅仅是对“EventAggregators”或“Messengers”的一些流行语引用——我知道它们与可能的解决方案有关,但我没有找到我能理解的好的文档……
最佳答案
实际上,这正是事件聚合器的工作。您在 Caliburn.Micro 中有一个内置的。
这很容易,MainViewModel
和 LoginViewModel
应该将聚合器作为依赖项:
private readonly IEventAggregator eventAggregator;
public MainViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
this.eventAggregator.Subscribe(this);
}
同样适用于 LoginViewModel
.这里有一点警告,它们都应该接收事件聚合器的相同实例,因此事件会正确传播(实际上,最好将 IoC 容器设置为注入(inject) IEventAggregator
作为单例) .
现在 MainViewModel
应该实现 IHandle<T>
, 其中T
是将用作消息的类,比方说:
public class LogInSuccessful
{
public readonly string LoggedInAs;
public LogInSuccessful(string loggedInAs)
{
LoggedInAs = loggedInAs;
}
}
然后
public class MainViewModel : ... , IHandle<LogInSuccessful>
{
....
public void Handle(LogInSuccessful message)
{
//here you can change the VM and access message.LoggedInAs string.
//This method will be called when there's an appropriate event published
//to the same event aggregator that the MainViewModel is subscribed to.
}
}
要发布事件,您必须在 LoginViewModel
中获取事件聚合器。然后在某个时候调用:
eventAggregator.Publish(new LogInSuccessful("Admin"));
进一步编辑
这样,LoginViewModel
只做一件事——验证凭据。如果它们有效,它会将事件发布到 MainViewModel
,它管理屏幕并应采取适当的操作。 LoginViewModel
不应“手动”更改主视图模型上的任何屏幕,这不是它的工作。
关于c# - 嵌套 View 模型 : Button actions and Communication?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15113163/