我们正在使用MVVM项目启动WPF,并且必须决定PRISM或MVVM Light(我对这两个框架都不熟悉).我已阅读了几篇文章但仍有几个问题.有人可以对框架中的以下几个方面有所了解吗?:
性能:出于任何原因,一个框架是否会比另一个框架表现更好?
应用程序内的通信(viewmodel到viewmodel或模块之间等):我已经读过MVVM Light有Messenging Service,它看起来也相当容易.但是PRISM似乎没有任何等价物.真的吗?PRISM如何处理交互?
单元测试:已经读过PRISM更好地支持单元测试.我们还可以在MVVM Light中编写NUNIT或VSTS测试吗?
Vladimir Al.. 20
我刚刚将一个项目从Prism转移到了MvvmLight,它看起来工作得更快(非常主观).
Prism和MvvmLight都有Mediator实现(Prvent中的IEventAggregator,MvvmLight中的IMessenger).但与IEventAggregator相比,IMessenger具有更多功能(例如,使用令牌发送消息),使用起来更方便(参见下一项).
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 { }
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);
}
}
棱镜的缺点:
它是非完全开源项目(官方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好得多.