prism vs mvvm light for wpf

后端 未结 3 608
清歌不尽
清歌不尽 2021-02-01 03:02

We are starting a WPF with MVVM project and have to decide on PRISM or MVVM Light (I am new to both these frameworks). I have read through a few posts but still have a few quest

3条回答
  •  北恋
    北恋 (楼主)
    2021-02-01 03:43

    1. I just moved a project from Prism to MvvmLight and it seems to work faster (very subjective).

    2. Both Prism and MvvmLight have Mediator realisation (IEventAggregator in Prism, IMessenger in MvvmLight). But IMessenger has more abilities (for instance, sending messages with tokens) compared to IEventAggregator and is much more convenient to use (see next item).

      MvvmLight also has a more powerful ViewModelBase class.

    3. Applications that use MvvmLight are much easier to test than those that use Prism. For instance, IMessenger is easier to mock than IEventAggregator.

    PrismViewModel.cs

    using System;
    using Microsoft.Practices.Prism.Events;
    using Microsoft.Practices.Prism.ViewModel;
    
    // An ugly empty event class
    public class StringEvent : CompositePresentationEvent { }
    
    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().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().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()
            // because it returns null when PrismViewModel's constructor
            // invokes GetEvent() method which leads to NullReferenceException
            return new PrismViewModel(eventAggregator ?? CreateEventAggregatorStub());
        }
    
        private static IEventAggregator CreateEventAggregatorStub()
        {
            var eventAggregatorStub = Substitute.For();
            eventAggregatorStub.GetEvent().Returns(Substitute.For());
            return eventAggregatorStub;
        }
    
        [Test]
        public void Constructor_WithNonNullEventAggregator_ExpectedSubscribesToStringEvent()
        {
            // Arrange
            var stringEventMock = Substitute.For();
    
            var eventAggregatorStub = Substitute.For();
            eventAggregatorStub.GetEvent().Returns(stringEventMock);
    
            // Act
            CreateViewModel(eventAggregatorStub);
    
            // Assert
            // With constrained isolation framework you can only mock virtual members
            // CompositePresentationEvent has only one virtual Subscribe overload with four parameters
            stringEventMock.Received()
                           .Subscribe(Arg.Any>(), Arg.Any(), Arg.Any(),
                                      Arg.Any>());
        }
    
        [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();
    
            var eventAggregatorStub = Substitute.For();
            eventAggregatorStub.GetEvent().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(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());
        }
    
        [Test]
        public void Constructor_WithNonNullMessenger_ExpectedRegistersToStringMessage()
        {
            var messengerStub = Substitute.For();
    
            var sut = CreateViewModel(messengerStub);
    
            messengerStub.Received().Register(sut, Arg.Any>());
        }
    
        [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();
            var sut = CreateViewModel(messengerMock);
            const string expectedMessage = "message";
    
            sut.SendMessage(expectedMessage);
    
            messengerMock.Received().Send(expectedMessage);
        }
    }
    

    Disadvantages of Prism:

    • it's non fully open-source project (official Prism repository is read-only)
      • 2015-10-30: now it's fully open-sourced: https://github.com/PrismLibrary/Prism
    • it no longer actively developed
      • 2015-10-30: new version of Prism: https://github.com/PrismLibrary/Prism
    • directly using of its classes leads to boiler-plate and less readable code

    I think that any new project should be based on modern solutions and approaches. IMHO, any modern MVVM-framework (like Catel, Caliburn.Micro, MvvmLight, ReactiveUI) is much better than Prism.

提交回复
热议问题