c# - 嵌套 View 模型 : Button actions and Communication?

标签 c# wpf mvvm caliburn.micro

这将是一个冗长的问题,但我长期以来一直通过构建丑陋的大型一体化类来避免这个问题。

例如,我正在编写一个采用 MVVM 设计的 WPF 独立应用程序(我也在使用 Caliburn.Micro),并且有一个带有 MainViewMainViewModel。此 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 LoginViewLoginView 会做您猜想的事情,即您可以输入(用户名、密码),并且有一个评估条目的按钮,如果它是正确的,我想切换设置 CentralVMLoggedInVM。但是按钮事件“存在于”LoginViewModelLoginVM 实例中,那么如何访问 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 中有一个内置的。

这很容易,MainViewModelLoginViewModel应该将聚合器作为依赖项:

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/

相关文章:

wpf - 如何在wpf中的组标题中显示属性名称

WPF ListBox ItemsSource StaticResource/Binding 问题

c# - 如何在 UWP 中使用 ListView 捕获点击?

silverlight - 无法在 xaml 中创建 View 模型实例

c# - ASP.MVC2 中选择要在 Controller 中显示的图像的最佳方法是什么

WPF验证未绑定(bind)的文本框

c# - 如何为 XML 编写 "filter"流包装器?

jquery - Kendo MVVM 绑定(bind)添加/插入项目,如何?

c# - 动态注释/属性值

c# - 是否可以从 C# 重定向 stdio sprintf_s?