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

本文关键字:信使 歧义 ViewModel 解决 | 更新日期: 2023-09-27 18:24:47

我有一个DropDownView,它包含如下所示的DropDownList

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

DropDownViewModel有两个属性,如下

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都属于一个单独的视图,并且不相互连接。(为了简单起见,只考虑创建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)。我只在一个ViewModel中测试了一个Messenger.Default.Register的代码,应用程序运行良好。我还测试了代码是否正在创建DropDownViewDropDownViewModel的多个实例。这方面似乎也没有什么问题。

我不明白为什么在多个ViewModel的情况下会出现这样的场景,因为每个ViewModel都有自己的DropDownViewDropDownViewModel实例。那么内部到底发生了什么呢?当创建了多个DropDownViewModel实例时,为什么应用程序在line1上的行为很奇怪?如何解决这个问题?

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

在我看来,这个问题与MVVM的WPF无关,而是与以下代码有关:

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

问题是Messenger。Default在我看来像一个静态类,而这个静态默认信使不会更改。这是正确的吗?我不能在你的机器上调试,但在我看来这是一种代码气味。

附带说明一下,您应该能够毫无问题地切换和更改视图模型。解决设计问题的方法是在视图模型中添加信使实例,这样就不会有共享的全局状态。

"当创建了多个DropDownViewModel实例时,为什么应用程序在第1行冻结?"

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

对于你的binging代码,你可以不写:"路径",只写:

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

它应该在我所知道的所有情况下都有效。

我想这就是您正在使用的MVVMLight。信使知道要向哪个收件人发送消息的定义因素是TMessage,即消息的类型:

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

所以用你的

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

ViewModel现在将接收字符串类型的任何消息。当您发送字符串类型的消息时,两种方法(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 });

在消息处理程序中,比较Ids:

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

然而,我的两点:忘记DropDownViewModel(在实际的ViewModel上同时保留SelectedItem和ItemsSource属性),完全避免使用Mediator模式。引入一个实现和处理INotifyPropertyChanged的基类,这样您就可以再次在一行中写入属性。这是我用的。