从另一个视图模型更新视图模型

9

我有两个ViewModels,一个附加到主窗口,另一个附加到通过单击主窗口上的按钮打开的子窗口。子窗口包含一个项目列表,并且我想通过更新主窗口视图模型来选择项目并在主窗口中显示它。最佳实现方法是什么?谢谢!


1
您特别要求“最佳”方法来做这件事。什么对您来说定义了“最佳”?它可能意味着最快,最简单,易于理解/调试,代码最少等。 - Josh G
“Simplest” 是非常主观的,但我倾向于在很大程度上基于对象之间真实关系的概念模型。如果它们是父/子关系,则直接引用可能非常简单明了。 - Josh G
1
在这个环境下,对我来说“最好”的意思是最简单的方法,因为我对WPF和MVVM非常陌生。 - codeBlue
我编辑了我的答案,列出我个人认为“最简单”的选择。 - Josh G
5个回答

13

有很多种方法可以实现这个功能。

  • 将主/父视图模型的引用传递给子视图模型,并让子视图模型调用主视图模型。

  • 使子视图模型触发一个事件,由父视图模型进行订阅。

  • 使用信使/中介者在两者之间进行通信。父视图模型进行订阅,子视图模型发布消息。这提供了松耦合。

  • 将主视图模型设置为全局服务。以某种方式进行注册。让子视图查找服务(需要全局服务是一个非常常见的问题),然后在全局/公共接口上调用某些内容。

根据我的经验,最简单的方法是第2种方法。在子视图模型上定义一个事件。父视图模型将不得不查找子视图模型(我不知道它是否包含在其中或者您的视图模型是如何构建的)并订阅该事件。


任何一种方法都可能是合适的。这取决于具体情况。除非虚拟机密切相关且概念上相关,否则耦合是不合适的。 - Josh G
非常有用的答案,我的MainViewModel类开始变得一团糟。谢谢你。 - Steel Nation

8

在ViewModel之间进行通信的标准方法是使用某种形式的消息传递。其中一种很好的实现方式是MVVM Light Toolkit

以下是使用默认信使的(随机)代码:

//Registering:
Messenger.Default.Register<IEnumerable<BookViewModel>>(this, true, fillSourceWith);
Messenger.Default.Register<DisplayModeEnum>(this, ChangeMainTemplates);
//with a specific "token"
Messenger.Default.Register<object>(this, MessageTokens.ClearList, o => BookSource.Clear()); 

//Sending
Messenger.Default.Send<List<BookViewModel>>(newBooks);
Messenger.Default.Send<DisplayModeEnum>(DisplayModeEnum.MediumLayout);
Messenger.Default.Send<object>(null, MessageTokens.ClearList);

他正在谈论父子关系。在这里使用消息传递不是一个好主意。 - Euphoric
3
我认为在这里使用消息传递会更好。如果不使用消息传递,你将不得不以某种方式耦合你的视图模型。 - Adam Rackis
消息传递可能合适,也可能不合适。对于这个问题来说,它可能过于复杂,但它是一个好的解决方案。 - Josh G
@Josh G - 我同意。根据他的情况,可能有其他同样有效的方法。你的回答很棒,可以给其他人提供帮助。 - Adam Rackis
他要求“最佳”解决方案,但“最佳”非常主观,需要更多关于他的上下文信息。 - Josh G

1

我几天前也遇到了同样的问题 ;-)

最终我使用了一个中介者来沟通两个视图模型。事实上,我使用了 MVVM Light 中的 Messenger。

public void Search(object parameter)
        {
            ChildWindow window = new ChildWindow();

            SearchWindow pageSearch = new SearchWindow();

            window.Content = pageSearch;
            window.Show();

            Messenger.Default.Register<Messages.CloseWindowMessage>(this, action => this.closeWindow(action));
        }

然后,我使用主窗口所需的所有信息定义了消息:

public class CloseWindowMessage : MessageBase
    {
        public bool Result { get; set; }
        public Model.Selected Selected { get; set; }
    }

最后,从子窗口返回的消息只需使用结果和要获取的对象进行注册即可。

您应该从视图的代码后台注册以关闭窗口。


你能完成你的示例,然后我就可以使用它了。你在哪里使用了这个搜索方法?如何从ViewModel注册到那里。一条消息可以发送什么?例如,如果我需要第二个VM中的一个变量,我该如何在第一个VM中访问它?谢谢。 - Ehsan

1

最好的方法是在子级和父级之间建立某种引用,并在关闭子级时更新此父级。

或者您可以在子级上设置某种事件,让父级监听此事件。然后在子级关闭时触发此事件。

当ViewModels在逻辑上不相关时,使用消息传递。

[抱怨]人们甚至不知道面向对象编程的基本原则吗??[/抱怨]


2
我认为在这种情况下使用通信工具并不一定是糟糕的设计。这取决于许多因素,但发送简单消息可能比在两个虚拟机之间建立面向对象关系更简单、更有效。 - Adam Rackis
1
我认为问题在于,在MVVM模式的一部分中,没有最佳实践方法来完成这个任务。 - user1228

0

我认为在两个视图模型之间传递消息的最佳方式是事件编程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接