1

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

例如,我正在编写一个带有 MVVM 设计的 WPF 独立应用程序(我也使用 Caliburn.Micro),并且有一个MainViewModel带有MainView. 这个视图包含一个StackPanel,它的内容StackPanel绑定到一个 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;}
}

现在,在MainViewModelI 集合的构造函数中

CentralVM = LoginVM

然后StackPanel自动绑定 View LoginView。会按照LoginView您的猜测进行,即您可以输入(用户名,密码),并且有一个评估条目的按钮,如果它是正确的,我想将设置切换CentralVMLoggedInVM. 但是按钮事件“存在”在 的LoginVM实例中LoginViewModel,那么如何访问 中的CentralVM属性MainViewModel

这当然只是一般问题的一个例子。我的第一个想法是执行以下操作:

-包含一个在单击按钮时设置LoginVM的属性(字符串类型) 。LoggedInAs-我向 中添加一个方法MainViewModel,如下所示:

private _loggedIn = false;
private void CheckForLoginChange() {
    if (_loggedIn == false && !String.IsNullOrEmpty(LoginVM.LoggedInAs)) {
        _loggedIn = true; 
        CentralVM = LoggedInVM;
    }
}

- 最后,我将此方法调用添加到 的设置器中LoginVM,即

public LoginViewModel LoginVM {
    get { return _logInVM; }
    private set { _logInVM = value; CheckForLoginChange(); }
}

但这不起作用。是不是因为虽然LoginVM点击按钮事件时发生了变化,但没有调用setter?

感谢您在这个方向上的任何帮助。我非常感谢一个详细的答案,而不仅仅是一些对“EventAggregators”或“Messengers”的流行语引用——我知道它们与可能的解决方案有关,但我还没有找到我能理解的好的文档......

4

2 回答 2

4

实际上,这正是 Event Aggregator 的工作。你有一个内置的 Caliburn.Micro。

这很容易,两者都MainViewModel应该LoginViewModel将聚合器作为依赖项:

private readonly IEventAggregator eventAggregator;
public MainViewModel(IEventAggregator eventAggregator)
{
    this.eventAggregator = eventAggregator;
    this.eventAggregator.Subscribe(this);
}

对于LoginViewModel. 这里有一点警告,它们都应该接收相同的事件聚合器实例,因此事件可以正确传播(实际上,最好将 IoC 容器设置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不应该“手动”更改主视图模型上的任何屏幕,这不是它的工作。

于 2013-02-27T13:29:14.323 回答
1

在回答您最后的评论时,您可以自定义引导程序,但老实说,如果您不打算使用 IoC 容器并且对该抽象级别不感兴趣,您可以只使用静态类来保存聚合器。

当然,您与实现耦合,但如果您只运行一个小项目并且对 DI/IoC 部分不感兴趣,它会做。

简单的类可能是

static class EventAggregatorProvider 
{ 
    private static EventAggregator _aggregator = new EventAggregator();

    public static EventAggregator Aggregator { get { return _aggregator; } }
}

然后在您的代码中,只需通过静态类访问它:

public void SomeMethod()
{
    // Do something
    EventAggregatorProvider.Aggregator.Publish(new SomeMessage());
}
于 2013-03-05T17:03:11.643 回答