【问题标题】:MVVM-Light Messenger With Multiple instances of the Same ViewModel具有相同 ViewModel 的多个实例的 MVVM-Light Messenger
【发布时间】:2013-08-28 00:05:23
【问题描述】:

我是 MVVM 的新手,但遇到了问题。我们正在使用 MVVM-Light 框架在 WPF 中编写数据库应用程序。程序状态的规范我们必须能够同时打开多个 ClaimView 实例。

为了打开新窗口,我们从 ViewModel 发送一条消息,该消息被捕获在 View 中,并打开新窗口。我们正在使用枚举令牌来识别正确的接收者来获取请求。

现在,如果我同时打开了 2 个 ClaimView 实例,并且我调用了 Messanger,它会打开 2 个相同的窗口,因为两个视图都在接收消息。

我们尝试在单独的线程上运行 ViewModel 的每个实例,并通过输出 ManagedThreadId 进行验证,两个实例仍在接收消息。

我们也取消了注册消息的注册,所以这不是问题。

任何帮助将不胜感激。

【问题讨论】:

    标签: c# wpf mvvm mvvm-light


    【解决方案1】:

    新答案

    正如 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 都在接收消息。您的意思是两个 View 都在接收消息吗?

    无论如何,如果您有两个实例使用相同的令牌注册相同的消息类型,那么这两个实例都会收到该消息。

    要解决这个问题,您首先需要 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;
        }
    }
    

    然后当你在你的视图中注册时(或者它是你的视图模型?),你需要从视图模型中获取 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,它仍然可以工作。

    【讨论】:

    • 感谢您的回复。首先我清理了帖子,所以现在应该更有意义了。其次,我正在尝试按照您在编辑中所说的去做,但是我的消息没有收到。也许我犯了一个错误。在我的 ViewModel 中,我正在这样做:Messenger.Default.Send&lt;int&gt;(Number, new MessageToken(this, Enumerators.OpenWindow.MainComent)); 在我的视图中,我注册了这个,其中 vm 是 DataContext:Messenger.Default.Register&lt;int&gt;(this, new MessageToken(vm, Enumerators.OpenWindow.MainComent), OpenCommentView); 消息正在发送但从未收到。
    • 想通了。该问题是由在视图和视图模型中创建新的 MessageToken 对象引起的。令牌中保存的数据相同,但实际令牌对象不同。我解决这个问题的方法是在 ViewModel 中将令牌创建为属性,然后在我看来,我从 DataContext 中获取了该属性。这似乎有效,如果您有更好的方法,请告诉我。
    • 啊,是的,这是有道理的。标记必须是完全相同的对象。
    • 我会更进一步,只创建一个空的 UniqueToken 类。 MVVMLight 框架将能够识别接收者,因为该 UniqueToken 的实例具有相同的引用。 MessengerToken.OpenWindow 然后可以移动到消息而不是令牌。
    猜你喜欢
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 2015-05-10
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    相关资源
    最近更新 更多