具有Veto选项的松散耦合的视图内模型通知

时间:2010-07-16 15:05:36

标签: c# wpf events architecture mvvm

在我正在构建的应用程序中,用户可以在一个视图中执行某些操作(由视图模型支持),该视图应该在一个或多个其他视图模型中触发操作。这些其他vms中的每一个都需要能够否决(a.k.a。取消)在第一个v / vm对中执行的操作。

示例:

  1. 用户点击帐户列表视图显示的DataGrid控件中的帐户。 DataGrid事件处理程序捕获单击并告诉vm。 Vm通知其他vms提议的更改。
  2. 由于用户已对另一个其他视图中的记录进行了未保存的编辑,因此其他vm首先告诉vm建议的所选帐户更改被拒绝。
  3. 当帐户列表vm收到此拒绝时,它会告知DataGrid将所选帐户设置为原样。如果未收到拒绝,则帐户列表vm将允许DataGrid选择项目更改。
  4. 类似的情况是用户启动应用程序关闭时。感兴趣的虚拟机需要一种方法来了解建议关机,并可以选择取消关机。

    视图模型应该松散耦合,因此它们之间的直接事件订阅是不可取的。

    您如何建议实施此视图内模型通信?

    使用事件聚合器来“广播”帐户更改事件是明智的做法吗?事件参数对象将包含bool Canceled属性。要取消更改的订阅vm将设置Canceled = true

    谢谢你,

2 个答案:

答案 0 :(得分:1)

我认为你的最后一段提出了一个很好的解决方案。

使用MVVM Light Toolkit,我会使用带有回调的消息来发送消息,并允许任意数量的订阅者通过取消来回拨。

public class AccountSelectedMessage : NotificationMessageAction<bool>
{
    public AccountSelectedMessage(Account selectedAccount, Action<bool> callback) : base("AccountSelectedWithCancelCallback", callback)
    {
        SelectedAccount = selectedAccount;
    }
    public AccountSelectedMessage(object sender, Account selectedAccount, Action<bool> callback) : base(sender, "AccountSelectedWithCancelCallback", callback)
    {
        SelectedAccount = selectedAccount;
    }
    public AccountSelectedMessage(object sender, object target, Account selectedAccount, Action<bool> callback) : base(sender, target, "AccountSelectedWithCancelCallback", callback)
    {
        SelectedAccount = selectedAccount;
    }

    public Account SelectedAccount { get; private set; }
}

public class AccountListViewModel : ViewModelBase
{
    public RelayCommand<Account> AccountSelectedCommand = new RelayCommand<Account>(AccountSelectedCommandExecute);

    private void AccountSelectedCommandExecute(Account selectedAccount)
    {
        MessengerInstance.Send(new AccountSelectedMessage(this, AccountSelectionCanceled));
    }

    private void AccountSelectionCanceled(bool canceled)
    {
        if (canceled)
        {
            // cancel logic here
        }
    }
}

public class SomeOtherViewModel : ViewModelBase
{
    public SomeOtherViewModel()
    {
        MessengerInstance.Register<AccountSelectedMessage>(this, AccountSelectedMessageReceived);
    }

    private void AccountSelectedMessageReceived(AccountSelectedMessage msg)
    {
        bool someReasonToCancel = true;
        msg.Execute(someReasonToCancel);
    }
}

正如您所看到的,此过程需要是异步的,并考虑到您不知道该消息的接收者可以取消多少,或者他们需要多长时间来响应。

答案 1 :(得分:1)

EventAggregator是要走的路。我们使用了Prism的那个。但我们没有采取所有的棱镜。只是与EventAggregator相关的类。我们确实为服务层程序集创建了一个DomainEvent,其中GUI引用不可用。