如何在ViewModels之间进行通信? (棱镜事件聚合器?)

时间:2018-03-20 01:31:23

标签: c# wpf mvvm prism publish-subscribe

我是C#和WPF的新手,我正在制作一个小应用程序,我认为这是学习和使用MVVM模式的好机会。

这肯定让事情变得比我想要的小应用程序更困难,但这是一个很好的学习经历。到目前为止,我已经能够跟进了。我似乎完全迷失了如何正确实现ViewModels相互交互的方式。

我有MainWindowView和OverlayView,我希望能够从主ViewModel在叠加视图上绘制一个矩形。我想要一种方法,只需从MainViewModel广播一个事件,让OverlayView听到它并按照指定绘制矩形。我也认为一旦我开始工作就添加'CloseAllWindows'广播会很方便。

从我的研究中,最简单的方法是使用像Prism这样的框架。我一直在尝试从Prism框架中建立一个Event Aggregator,但无论我尝试什么,我都无法让它工作。我看了很多文章或教程,但它们似乎都已经过时,链接断了,或者我似乎无法理解它们并让它们发挥作用。

这是我到目前为止所得到的:

Events.cs

2018-03-19 18:24:28.079 python[84266:4693971] _createMenuRef called with existing principal MenuRef already associated with menu
2018-03-19 18:24:28.081 python[84266:4693971] (
    0   CoreFoundation                      0x00007fff8b693452 __exceptionPreprocess + 178
    1   libobjc.A.dylib                     0x00007fff9e6d073c objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff8b6fa49d +[NSException raise:format:] + 205
    3   AppKit                              0x00007fff9ebf0c86 -[NSCarbonMenuImpl _createMenuRef] + 62
    4   AppKit                              0x00007fff9ebf05c5 -[NSCarbonMenuImpl _instantiateCarbonMenu] + 140
    5   AppKit                              0x00007fff9ebee270 -[NSApplication finishLaunching] + 856
    6   AppKit                              0x00007fff9ebedbbd -[NSApplication run] + 231
    7   _macosx.so                          0x0000000106e451c8 show + 216
    8   libpython3.5m.dylib                 0x00000001000bd34e PyEval_EvalFrameEx + 22542
    9   libpython3.5m.dylib                 0x00000001000c192f fast_function + 207
    10  libpython3.5m.dylib                 0x00000001000bd434 PyEval_EvalFrameEx + 22772
    11  libpython3.5m.dylib                 0x00000001000c10c3 _PyEval_EvalCodeWithName + 1779
    12  libpython3.5m.dylib                 0x00000001000b7b1e PyEval_EvalCodeEx + 78
    13  libpython3.5m.dylib                 0x000000010003430f function_call + 351
    14  libpython3.5m.dylib                 0x000000010000fd73 PyObject_Call + 99
    15  libpython3.5m.dylib                 0x000000010002130d method_call + 141
    16  libpython3.5m.dylib                 0x000000010000fd73 PyObject_Call + 99
    17  libpython3.5m.dylib                 0x0000000100064c55 slot_tp_call + 117
    18  libpython3.5m.dylib                 0x000000010000fd73 PyObject_Call + 99
    19  libpython3.5m.dylib                 0x00000001000bdec8 PyEval_EvalFrameEx + 25480
    20  libpython3.5m.dylib                 0x00000001000c10c3 _PyEval_EvalCodeWithName + 1779
    21  libpython3.5m.dylib                 0x00000001000c19ae fast_function + 334
    22  libpython3.5m.dylib                 0x00000001000bd434 PyEval_EvalFrameEx + 22772
    23  libpython3.5m.dylib                 0x00000001000c10c3 _PyEval_EvalCodeWithName + 1779
    24  libpython3.5m.dylib                 0x00000001000b7ac1 PyEval_EvalCode + 81
    25  libpython3.5m.dylib                 0x00000001000e6937 PyRun_FileExFlags + 215
    26  libpython3.5m.dylib                 0x00000001000e60ea PyRun_SimpleFileExFlags + 842
    27  libpython3.5m.dylib                 0x00000001000fcc5b Py_Main + 3355
    28  python                              0x0000000100000dc7 main + 215
    29  python                              0x0000000100000ce4 start + 52
    30  ???                                 0x0000000000000002 0x0 + 2
)

MainWindowViewModel.cs

using System.Windows;
using Prism.Events;

namespace Application.Overlay {
    public class DrawRectEvent : PubSubEvent<Rect> {
    }
}

MainWindow.xaml.cs

 namespace Application.ViewModels {

    public class MainWindowViewModel : BindableBase {

        private IEventAggregator eventAggregator;

        public MainWindowViewModel(IEventAggregator eventAggregator) {
            this.eventAggregator = eventAggregator;

            WindowLoadedCommand = new RelayCommand(WindowLoaded);
            TestDrawCommand = new RelayCommand(TestDraw);
        }

        public RelayCommand TestDrawCommand { get; set; }
        public void TestDraw() {
            Console.WriteLine("Publishing DrawRect Broadcast");
            Size size = new Size(100, 100);
            Point point = new Point(50, 50);
            Rect rect = new Rect(point, size);

            eventAggregator.GetEvent<DrawRectEvent>().Publish(rect);
        }
}

OverlayViewModel.cs

namespace Application {

    public partial class MainWindow : Window {
        private IEventAggregator eventAggregator;
        TextBoxOutputter outputter;

        public MainWindow() {
            this.DataContext = new ViewModels.MainWindowViewModel(eventAggregator);
            InitializeComponent();

        // Route console output to ConsoleOutput TextBox
        outputter = new TextBoxOutputter(ConsoleOutput);
        Console.SetOut(outputter);
    }

OverlayView.xaml.cs

namespace Application.Overlay : BindableBase {

    public class OverlayViewModel {
    private IEventAggregator eventAggregator;

    public OverlayViewModel(IEventAggregator eventAggregator) {

        this.eventAggregator = eventAggregator;
        this.eventAggregator.GetEvent<DrawRectEvent>().Subscribe(DrawRectEventHandler);
        }

        private void DrawRectEventHandler(Rect rect) {
            Console.WriteLine("Signal Received, Drawing Rectangle!");
        }

使用这段代码编译得很好,但我得到一个“Null引用异常”“对象引用没有设置为对象的in`stance”运行它时,它指的是OverlayViewModel中的第9行:

namespace Application.Overlay {

    public partial class Overlay : Window {

        private IEventAggregator eventAggregator;

        public Overlay() {
            InitializeComponent();
            Top = 0;
            Left = 0;
            this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
            this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            this.WindowStyle = WindowStyle.None;

            this.DataContext = new OverlayViewModel(eventAggregator);
        }

我不确定我是否将正确的内容传递给代码隐藏中的新ViewModel实例,但我不确定你还传递了什么。

如果你看到我做错了什么或者你有一个最新的指南,显示出比我正在做的更有效的方式,那么我真的很感激。谢谢!

1 个答案:

答案 0 :(得分:0)

由于您刚刚开始,我不会过于深入地解释基础知识,我将直接跳到'我们如何让您的当前代码构建和工作'的部分,你可以继续学习。

从我提供的代码中我可以看出,看起来你从未真正创建过EventAggregator的实例(除非你设置了一个IoC容器,我看不到任何迹象表明你已经完成了这一点。

所以在你的代码::

MainWindow.xaml.cs

public partial class MainWindow : Window {
    private IEventAggregator eventAggregator;
    TextBoxOutputter outputter;

    public MainWindow() {
        this.DataContext = new ViewModels.MainWindowViewModel(eventAggregator);
        InitializeComponent();

    // Route console output to ConsoleOutput TextBox
    outputter = new TextBoxOutputter(ConsoleOutput);
    Console.SetOut(outputter);
}

您正在创建ViewModels.MainWindowViewModel的实例并传入空字段eventAggregator。在此之前,您需要使用实际实例填充该字段。所以试试这个::

public partial class MainWindow : Window {
    private IEventAggregator eventAggregator;
    TextBoxOutputter outputter;

    public MainWindow() {
        this.eventAggregator = new EventAggregator(); //<---
        this.DataContext = new ViewModels.MainWindowViewModel(eventAggregator);
        InitializeComponent();

    // Route console output to ConsoleOutput TextBox
    outputter = new TextBoxOutputter(ConsoleOutput);
    Console.SetOut(outputter);
}