无法解决多个ViewModel中信使的歧义

时间:2014-09-26 06:09:36

标签: c# wpf mvvm

我有一个DropDownView,其中包含DropDownList,如下所示

<ComboBox Grid.Column="1" ItemsSource="{Binding Path=MyList}"
          SelectedItem="{Binding Path=Item}"  Height="30"/>

DropDownViewModel有2个属性如下

private ObservableCollection<string> _myList;
public ObservableCollection<string> MyList {
    get { return _myList; }
    set {
        if (_myList == value)
            return;

        _myList = value;
        RaisePropertyChanged("MyList");
    }
}

private string _item;
public string Item {
    get { return _item; }
    set {
        if (_item == value)
            return;

        _item = value;
        Messenger.Default.Send(_item);  //line1
        RaisePropertyChanged("Item");
    }
}

现在,我有多个ViewModel,它创建了这个DropdownViewModel的多个实例,如下所示。每个ViewModel都属于一个单独的View,并且不会互连。 (为简单起见,只考虑创建2个视图模型。)

ViewModel1

public class ViewModel1
{
    private readonly DropDownViewModel _ddVM1;
    public ViewModel1(){
        _ddVM1 = new DropDownViewModel();
        Messenger.Default.Register<string>(this, this.GetItem1);
    }

    private void string GetItem1(string obj){
        //perform some function
    }

}

ViewModel2

public class ViewModel2 
{
    private readonly DropDownViewModel _ddVM2;
    public ViewModel2(){
        _ddVM2 = new DropDownViewModel();
        Messenger.Default.Register<string>(this, this.GetItem2);
    }

    void string GetItem2(string obj){
        //perform some function
    }
}

现在,当我运行应用程序并从任何一个View的DropDownList中选择一个值时,始终会调用第一个注册函数(在本例中为GetItem1)。我只在一个Messenger.Default.Register中测试了只有一个ViewModel的代码,应用运行正常。我还测试了代码是否正在创建DropDownViewDropDownViewModel的多个实例。那个地区似乎也没有问题。

我不明白为什么在多个ViewModel的情况下出现这种情况,因为每个ViewModel都有自己的DropDownViewDropDownViewModel实例。那么内部究竟发生了什么?当创建多个line1实例时,为什么应用程序在DropDownViewModel上以奇怪的方式运行?如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

在我看来,问题不是与MVVM相关的WPF,而是与此代码相关:

Messenger.Default.Send(_item);  //line1

问题是Messenger.Default看起来像一个静态类,这个静态默认信使不会改变。这是对的吗?我无法在你的机器上进行调试,但这看起来像是一种代码味道。

作为旁注,您应该能够毫无问题地切换和更改视图模型。要解决您的设计问题,请在视图模型中添加您的信使实例,这样您就不会拥有共享的全局状态。

&#34;为什么在创建了多个DropDownViewModel实例时应用程序冻结在第1行?&#34;

很可能与WPF代码无关,但与Messenger.Default.Send方法代码无关。

对于你的binging代码,你可以不用编写:&#34; Path&#34;只是:

<ComboBox Grid.Column="1" ItemsSource="{Binding MyList}"
      SelectedItem="{Binding Item}"  Height="30"/>

它应该适用于我所知道的所有情况。

答案 1 :(得分:0)

我想这是你正在使用的MVVMLight。 Messenger知道哪些收件人发送邮件的定义因素是TMessage,邮件的类型:

public virtual void Register<TMessage>(object recipient, Action<TMessage> action)

所以用你的行

Messenger.Default.Register<string>(this, this.GetItem1);

ViewModel现在将收到任何类型为string的消息。发送字符串类型消息时,将执行这两种方法(GetItem1GetItem2)。

正常情况是每个Message都有一个复杂的类型,所以你可以使用类似的东西,DropDownId镜像你附加到每个DropDownViewModel的唯一标识符。

public class DropDownSelectedItemMessage
{
    public string DropDownId { get; set; }
    public string SelectedItem { get; set; }
}

然后你就像这样注册

Messenger.Default.Register<DropDownSelectedItemMessage>(this, this.GetItem1);

发送消息

Messenger.Default.Send(new DropDownSelectedItemMessage() { DropDownId = _id, SelectedItem = _item });

并在消息处理程序中,您比较ID:

private void string GetItem1(DropDownSelectedItemMessage message)
{
    if (message.DropDownId == _ddVM1.Id)
    {
        //perform some function
    }
}

然而,我的两分钱:忘掉DropDownViewModel(在你的实际ViewModel上保留SelectedItem和ItemsSource属性),完全不使用Mediator模式。引入实现和处理INotifyPropertyChanged的基类,以便您可以再次在一行上写入属性。 Here我使用的是什么。