我在MVVM上相对较新,并遇到了一个问题。我们正在使用MVVM-Light框架在WPF中编写数据库应用程序。程序状态的规范我们必须能够立即打开ClaimView的多个实例。
要打开新窗口,我们将从View中捕获的ViewModel发送一条消息,然后打开新窗口。我们使用枚举令牌来识别正确的收件人以获取请求。
现在,如果我同时打开了2个ClaimView实例,并且我调用了Messanger,它会打开2个相同的窗口,因为两个视图都在接收消息。
我们尝试在单独的线程上运行ViewModel的每个实例,并通过输出ManagedThreadId进行验证,并且两个实例仍然收到消息。
我们也注册了已注册的消息,因此这不是问题。
任何帮助都将不胜感激。
答案 0 :(得分:7)
新答案
正如OP(Daryl)指出的那样,我的原始答案(见下文)并不完全正确,所以我提供了一个新的答案,以防有人遇到同样的问题:
有意义的是,如果您有两个实例注册具有相同令牌的相同消息类型,则两个实例都将收到该消息。解决方案是提供每个View-ViewModel对唯一的令牌。
您可以将枚举值放在类中,而不是仅使用普通枚举值作为标记:
public class UniqueToken
{
public MessengerToken Token { get; private set; }
public UniqueToken(MessengerToken token)
{
Token = token;
}
}
然后在ViewModel中添加一个新属性来存储其中一个唯一标记:
// add a property to your ViewModel
public UniqueToken OpenWindowToken { get; private set; }
// place this in the constructor of your ViewModel
OpenWindowToken = new UniqueToken(MessengerToken.OpenWindow);
// in the appropriate method, send the message
Messenger.Send(message, OpenWindowToken);
最后,在您的视图中,您现在可以获取唯一标记并使用它来注册OpenWindow消息:
var viewModel = (MyViewModel)DataContext;
var token = viewModel.OpenWindowToken;
Messenger.Register<TMessage>(this, token, message => OpenWindow(message));
ViewModel和View都必须使用UniqueToken的单个实例,因为如果接收者令牌和发件人令牌是完全相同的对象,信使将仅发送消息,而不仅仅是具有相同属性值的实例。
原始答案(不太正确)
我认为你的问题可能有一个拼写错误:你说打开一个新窗口,你从ViewModel向View发送一条消息,但后来你说两个 ViewModels 正在接收消息。你的意思是两个观点都收到了消息吗?
在任何情况下,如果你有两个使用相同令牌注册相同消息类型的实例,那么两个实例都将收到消息。
要解决此问题,您首先需要ViewModel的每个实例都具有唯一ID。这可以通过Guid
完成。类似的东西:
// add a property to your ViewModel
public Guid Id { get; private set; }
// place this in the constructor of your ViewModel
Id = Guid.NewGuid();
然后你需要你的令牌成为一个具有两个属性的对象:一个用于guid,一个用于枚举值:
public class UniqueToken
{
public Guid Id { get; private set; }
public MessengerToken Token { get; private set; }
public UniqueToken(Guid id, MessengerToken token)
{
Id = id;
Token = token;
}
}
然后当您在View中注册(或者它是您的ViewModel?)时,您需要从ViewModel中获取Guid。这可以这样工作:
var viewModel = (MyViewModel)DataContext;
var id = viewModel.Id;
var token = new UniqueToken(id, MessengerToken.OpenWindow);
Messenger.Register<TMessage>(this, token, message => OpenWindow(message));
最后,在您的ViewModel中,您需要执行以下操作:
var token = new UniqueToken(Id, MessengerToken.OpenWindow);
Messenger.Send(message, token);
修改强>
在输入所有内容后,我突然意识到您在ViewModel上并不需要Id
属性。您可以使用ViewModel本身作为唯一标识符。因此,对于UniqueToken
,您只需将public Guid Id
替换为public MyViewModel ViewModel
,它仍应有效。