wpf - wpf的 Prism vs mvvm灯

标签 wpf mvvm prism mvvm-light

我们正在使用 MVVM 项目启动 WPF,并且必须决定使用 PRISM 或 MVVM Light(我对这两个框架都是新手)。我已经阅读了一些帖子,但仍有一些问题。有人可以对以下方面进行一些说明吗?两个框架?:

  • 性能:无论出于何种原因,任何一个框架都会比另一个框架表现得更好吗?
  • 应用程序内的通信(viewmodel 到 viewmodel 或模块之间等):我读过 MVVM Light 有消息服务,这似乎也相当容易。但 PRISM 似乎没有任何等价物。真的吗? PRISM 将如何处理交互?
  • 单元测试:已阅读 PRISM 更好地支持单元测试。我们还能在 MVVM Light 中编写 NUNIT 或 VSTS 测试吗?
  • 最佳答案

  • 我刚刚将一个项目从 Prism 转移到 MvvmLight,它似乎工作得更快(非常主观)。
  • Prism 和 MvvmLight 都有 Mediator 实现(Prism 中的 IEventAggregator,MvvmLight 中的 IMessenger)。但与 IEventAggregator 相比,IMessenger 具有更多功能(例如,使用 token 发送消息)并且使用起来更加方便(参见下一项)。

    MvvmLight 还有一个更强大的 ViewModelBase 类。
  • 使用 MvvmLight 的应用程序比使用 Prism 的应用程序更容易测试。例如,IMessenger 比 IEventAggregator 更容易模拟。

  • PrismViewModel.cs

    using System;
    using Microsoft.Practices.Prism.Events;
    using Microsoft.Practices.Prism.ViewModel;
    
    // An ugly empty event class
    public class StringEvent : CompositePresentationEvent<string> { }
    
    public sealed class PrismViewModel : NotificationObject
    {
        private readonly IEventAggregator _eventAggregator;
    
        private string _name;
    
        public PrismViewModel(IEventAggregator eventAggregator)
        {
            if (eventAggregator == null)
                throw new ArgumentNullException("eventAggregator");
    
            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<StringEvent>().Subscribe(s => Name = s);
        }
    
        public string Name
        {
            get { return _name; }
            set
            {
                // boiler-plate code
                if (value == _name) 
                    return;
                _name = value;
                RaisePropertyChanged(() => Name);
            }
        }
    
        public void SendMessage(string message)
        {
            _eventAggregator.GetEvent<StringEvent>().Publish(message);
        }
    }
    

    PrismViewModelTestCase.cs

    using System;
    using FluentAssertions;
    using Microsoft.Practices.Prism.Events;
    using NSubstitute;
    using NUnit.Framework;
    
    public class PrismViewModelTestCase
    {
        private static PrismViewModel CreateViewModel(IEventAggregator eventAggregator = null)
        {
            // You can't return Substitute.For<IEventAggregator>()
            // because it returns null when PrismViewModel's constructor
            // invokes GetEvent<StringEvent>() method which leads to NullReferenceException
            return new PrismViewModel(eventAggregator ?? CreateEventAggregatorStub());
        }
    
        private static IEventAggregator CreateEventAggregatorStub()
        {
            var eventAggregatorStub = Substitute.For<IEventAggregator>();
            eventAggregatorStub.GetEvent<StringEvent>().Returns(Substitute.For<StringEvent>());
            return eventAggregatorStub;
        }
    
        [Test]
        public void Constructor_WithNonNullEventAggregator_ExpectedSubscribesToStringEvent()
        {
            // Arrange
            var stringEventMock = Substitute.For<StringEvent>();
    
            var eventAggregatorStub = Substitute.For<IEventAggregator>();
            eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);
    
            // Act
            CreateViewModel(eventAggregatorStub);
    
            // Assert
            // With constrained isolation framework you can only mock virtual members
            // CompositePresentationEvent<TPayload> has only one virtual Subscribe overload with four parameters
            stringEventMock.Received()
                           .Subscribe(Arg.Any<Action<string>>(), Arg.Any<ThreadOption>(), Arg.Any<bool>(),
                                      Arg.Any<Predicate<string>>());
        }
    
        [Test]
        public void Name_ExpectedRaisesPropertyChanged()
        {
            var sut = CreateViewModel();
            sut.MonitorEvents();
    
            sut.Name = "any-value";
    
            sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
        }
    
        [Test]
        public void SendMessage_ExpectedPublishesStringEventThroughEventAggregator()
        {
            // Arrange
            var stringEventMock = Substitute.For<StringEvent>();
    
            var eventAggregatorStub = Substitute.For<IEventAggregator>();
            eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);
    
            var sut = CreateViewModel(eventAggregatorStub);
            const string expectedPayload = "any-string-payload";
    
            // Act
            sut.SendMessage(expectedPayload);
    
            // Assert
            stringEventMock.Received().Publish(expectedPayload);
        }
    }
    

    MvvmLightViewModel.cs

    using System;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Messaging;
    
    public sealed class MvvmLightViewModel : ViewModelBase
    {
        private string _name;
    
        public MvvmLightViewModel(IMessenger messenger)
        {
            if (messenger == null)
                throw new ArgumentNullException("messenger");
    
            // ViewModelBase already have field for IMessenger
            MessengerInstance = messenger; 
            MessengerInstance.Register<string>(this, s => Name = s);
        }
    
        public string Name
        {
            get { return _name; }
            set { Set(() => Name, ref _name, value); // Chic!  }
        }
    
        public void SendMessage(string message)
        {
            MessengerInstance.Send(message);
        }
    }
    

    MvvmLightViewModelTestCase.cs

    using System;
    using FluentAssertions;
    using GalaSoft.MvvmLight.Messaging;
    using NSubstitute;
    using NUnit.Framework;
    
    public class MvvmLightViewModelTestCase
    {
        private static MvvmLightViewModel CreateViewModel(IMessenger messenger = null)
        {
            return new MvvmLightViewModel(messenger ?? Substitute.For<IMessenger>());
        }
    
        [Test]
        public void Constructor_WithNonNullMessenger_ExpectedRegistersToStringMessage()
        {
            var messengerStub = Substitute.For<IMessenger>();
    
            var sut = CreateViewModel(messengerStub);
    
            messengerStub.Received().Register(sut, Arg.Any<Action<string>>());
        }
    
        [Test]
        public void Name_ExpectedRaisesPropertyChanged()
        {
            var sut = CreateViewModel();
            sut.MonitorEvents();
    
            sut.Name = "any-value";
    
            sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
        }
    
        [Test]
        public void SendMessage_ExpectedSendsStringMessageThroughMessenger()
        {
            var messengerMock = Substitute.For<IMessenger>();
            var sut = CreateViewModel(messengerMock);
            const string expectedMessage = "message";
    
            sut.SendMessage(expectedMessage);
    
            messengerMock.Received().Send(expectedMessage);
        }
    }
    

    Prism 的缺点:
  • 它不是完全开源的项目(官方 Prism 存储库是只读的)
  • 2015-10-30 : 现在完全开源了: https://github.com/PrismLibrary/Prism
  • 它不再积极发展
  • 2015-10-30 : 新版 Prism: https://github.com/PrismLibrary/Prism
  • 直接使用它的类会导致样板和可读性较差的代码

  • 我认为任何新项目都应该基于现代解决方案和方法。
    恕我直言,任何现代 MVVM 框架(如 Catel、Caliburn.Micro、MvvmLight、ReactiveUI)都比 Prism 好得多。

    关于wpf - wpf的 Prism vs mvvm灯,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20487243/

    相关文章:

    c# - 使用 PRISM 的 MVVM 模式上下文中的命令方法是什么?

    wpf - 如何设置 ListView 项目的样式?

    c# - 如何在 SharpDevelop 4.2 中启动一个国际化的 WPF 项目?

    c# - "Object reference not set to an instance of an object"in PresentationFramework with Live Shaping

    wpf - 如何动态加载用户控件?

    wpf - 我可以从其他模型继承 MVVM 模型吗?

    c# - 找不到 Prism.Navigation.TabbedPages 命名空间

    wpf - 无法加载 FsXaml.Wpf.TypeProvider

    c# WPF MVVM TabControl 与多个 ViewModels 和更改选项卡

    c# - 如何在 Prism 框架中的模块之间正确发送事件消息?