无法解决多个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
的代码,应用程序运行良好。我还测试了代码是否正在创建DropDownView
和DropDownViewModel
的多个实例。这方面似乎也没有什么问题。
我不明白为什么在多个ViewModel的情况下会出现这样的场景,因为每个ViewModel
都有自己的DropDownView
和DropDownViewModel
实例。那么内部到底发生了什么呢?当创建了多个DropDownViewModel
实例时,为什么应用程序在line1
上的行为很奇怪?如何解决这个问题?
在我看来,这个问题与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现在将接收字符串类型的任何消息。当您发送字符串类型的消息时,两种方法(GetItem1
和GetItem2
)都会执行。
通常情况下,每个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的基类,这样您就可以再次在一行中写入属性。这是我用的。