棱镜与mvvm光为wpf

时间:2013-12-10 05:58:05

标签: wpf mvvm prism mvvm-light

我们正在使用MVVM项目启动WPF,并且必须决定PRISM或MVVM Light(我对这两个框架都不熟悉)。我已阅读了几篇文章但仍有几个问题。有人可以请注意以下几个方面:w.r.t。两个框架?:

  1. 性能:出于某种原因,一个框架是否会比另一个框架表现更好?

  2. 应用程序内的通信(viewmodel到viewmodel或模块​​之间等):我已经读过MVVM Light有Messenging Service,它看起来也相当容易。但是PRISM似乎没有任何等价物。真的吗? PRISM将如何处理交互?

  3. 单元测试:已经读过PRISM更好地支持单元测试。我们还可以在MVVM Light中编写NUNIT或VSTS测试吗?

3 个答案:

答案 0 :(得分:19)

  1. 我刚将一个项目从Prism转移到MvvmLight,它看起来工作得更快(非常主观)。

  2. Prism和MvvmLight都有Mediator实现(Prvent中的IEventAggregator,MvvmLight中的IMessenger)。但与IEventAggregator相比,IMessenger具有更多功能(例如,使用令牌发送消息),并且使用起来更方便(参见下一项)。

    MvvmLight还有一个更强大的ViewModelBase类。

  3. 使用MvvmLight的应用程序比使用Prism的应用程序更容易测试。例如,IMessenger比IEventAggregator更容易模拟。

  4. 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);
        }
    }
    

    棱镜的缺点:

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

答案 1 :(得分:6)

你不能完全比较Prism和MvvmLight。

Prism更多的是关于应用程序架构,即使Prism被称为MVVM框架。实际上直到Prism 5它与MVVM无关,它在Prism 4.1和之前的版本中没有BaseViewModel类。

Prism不是一个MVVM框架,它应用程序框架比它更高。 Prism 5引入了对MVVM的一些支持,而Prism 6则更进一步。

MVVM只是棱镜提供指导解决问题的另一个方面。

这就像比较Angular vs. Knockout。 AngularJS管理整个应用程序并定义应用程序代码应该如何构建的指南,而使用KnockoutJS,应用程序结构完全取决于您。这是Prism和MvvmLight之间的类似案例。

Prism提供了一组设计模式的实现,这些模式有助于编写结构良好且可维护的XAML应用程序,包括MVVM,依赖注入,命令,事件聚合等。 Prism的核心功能是针对这些平台的可移植类库中的共享代码库; WPF,Windows 10 UWP和Xamarin Forms。

如果您使用wpf开发企业级应用程序,我建议使用Prism。

请观看有关MVVM Made Simple with Prism的网络研讨会。主持人是Brian Lagunas: https://www.youtube.com/watch?v=ZfBy2nfykqY

答案 2 :(得分:3)

我不相信MS曾经将PRISM推广为一个&#34;框架&#34;与MVVM Light,Caliburn等广告相同的意义。&#34;我的理解是PRISM总是被呈现给&#34; world&#34;作为一种练习。&#34;我认为,这只是一种有组织的应用程序构建方式。它变得有点混乱,因为PRISM提供的所有代码都可以认为它是一个&#34;框架&#34;可用于构建应用程序。而且,它是真的,您可以使用PRISM提供的代码来构建您自己的应用程序。但是,在我看来,PRISM比#34;框架更加复杂。 MVVM可用,并且有一个陡峭的学习曲线以及&#34; overkill&#34;并使您的应用程序比必要的更复杂。如果你有时间学习最新的&#34;框架&#34;或&#34;练习&#34;在您构建应用程序时,这太棒了!我的经验是,我没有考虑学习一些新的&#34;练习&#34;或最新的&#34;框架&#34;但必须完成工作。在一个理想的世界里,人们希望能够使用最新最好的框架#34;或采用最新的&#34;做法&#34;但有时你只需要坚持你已经知道并完成它。