WPF MVVM:从ViewModel正确触发View上的事件的方法

22
在我的WPF应用程序中有两个窗口(每个窗口都有自己的ViewModel):
  1. 应用程序的主窗口显示带有许多单词的列表(绑定到MainViewModel)

  2. 对话框窗口允许用户将新项目添加到列表中(绑定到AddWordViewModel)

MainViewModel具有Articles属性,它是List类型(此集合由其中一个服务类填充),并绑定到主窗口的ListBox上。

AddWordViewModel具有SaveWordCommand,该命令绑定到“添加单词”对话框的保存按钮。其任务是获取用户输入的文本并将其传递给服务类。

用户点击保存按钮后,我需要通知MainViewModel从服务重新加载Articles。

我的想法是在MainViewModel中公开公共命令,并从AddWordViewModel中执行它。

实现它的正确方法是什么?

谢谢!

1个回答

19

事件聚合器是解决这种问题的一种很好的方式。基本上,有一个中央类(为了简单起见,我们假设它是一个Singleton,并面对反对Singleton的人可能会带来的愤怒),负责将事件从一个对象传递到另一个对象。使用您的类名,使用可能如下:

public class MainViewModel
{
    public MainViewModel()
    {
        WordAddedEvent event = EventAggregator.Instance.GetEvent<WordAddedEvent>();
        event.Subscribe(WordAdded);
    }

    protected virtual void WordAdded(object sender WordAddedEventArgs e)
    {
        // handle event
    }
}

public class AddWordViewModel
{    
    //From the command
    public void ExecuteAddWord(string word)
    {
        WordAddedEvent event = EventAggregator.Instance.GetEvent<WordAddedEvent>();
        event.Publish(this, new WordAddedEventArgs(word));
    }
}

这种模式的优点是,您可以非常轻松地扩展应用程序以创建多种单词,并且对已添加的单词感兴趣的多个ViewModel,两者之间没有耦合,因此您可以根据需要添加和删除它们。
如果你想避免单例模式(我建议你这样做以进行测试),那么值得考虑依赖注入,不过这确实是另一个问题。

好的,最后一点想法。我从重新阅读你的问题中看到,你已经有了某种处理Word对象的Word Service类来处理检索和存储。由于两个ViewModel已经与该服务耦合,因此该服务可以负责在添加新单词时引发事件。尽管我仍然建议EventAggregator更灵活且更好,但是YAGNI可能适用于此处。

public class WordService
{
    public event EventHandler<WordAddedEventArgs> WordAdded;

    public List<string> GetAllWords()
    {
        //return words
    }

    public void SaveWord(string word)
    {
        //Save word
        if (WordAdded != null) WordAdded(this, new WordAddedEventArgs(word));
        //Note that this way you lose the reference to where the word really came from
        //probably doesn't matter, but might
    }
}

public class MainViewModel
{
    public MainViewModel()
    {
        //Add eventhandler to the services WordAdded event
    }
}

但是你要避免的是,在一个ViewModel上调用另一个ViewModel的命令,这会导致ViewModel之间的耦合,严重限制了扩展应用程序的选项(如果第二个ViewModel对新单词感兴趣,现在AddWordViewModel负责告诉它吗?)


非常感谢您提供如此详细的答案。我需要深入研究一下 :) 接下来会有很多乐趣 :) - Daniil Harik
我在MVVM实现中采用了完全的面向对象编程,使用了一个庞大的对象图。两年后进行结构性更改变得非常困难。我希望当初采用聚合器/中介者模式,因为它具有巨大的灵活性。注意:内存泄漏。确保您的聚合器使用弱引用。 - Gusdor

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