wpf的棱镜vs mvvm灯

Posted

技术标签:

【中文标题】wpf的棱镜vs mvvm灯【英文标题】:prism vs mvvm light for wpf 【发布时间】:2013-12-27 13:02:27 【问题描述】:

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

    性能:无论出于何种原因,其中一个框架的性能是否优于另一个?

    应用程序内的通信(viewmodel 到 viewmodel 或模块之间等):我读过 MVVM Light 具有消息服务,这似乎也相当容易。但 PRISM 似乎没有任何等价物。真的吗? PRISM 将如何处理交互?

    单元测试:已阅读 PRISM 更好地支持单元测试。我们还能在 MVVM Light 中编写 NUNIT 或 VSTS 测试吗?

【问题讨论】:

回答您的 2):Prism 有一个 EventAggregator,它执行您描述的 MVVM 所做的事情。 除了emedbo的评论,您可以在Loosely Coupled Communication的Prism指南章节中找到更多Prism交流的相关信息 MSDN. PRISM 的最佳功能之一是Regions。它有EventAggregator 用于ViewModelViewModel 通讯。您可以使用混合行为(System.Windows.Interactivity、Microsoft.Expression.Interactions)进行交互。单元测试取决于您编写ViewModel 单元测试的友好程度,而不是取决于您使用的 MVVM 框架。因此,如果您确实希望在您的应用程序中支持Region,那么请使用 PRISM,否则使用 MVVM-Light。不知道有没有办法也可以用 MVVM-Light 实现 Regions。 【参考方案1】:

    我刚刚将一个项目从 Prism 转移到 MvvmLight,它似乎工作得更快(非常主观)。

    Prism 和 MvvmLight 都有 Mediator 实现(Prism 中的 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<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 官方仓库是只读的) 2015-10-30:现已完全开源:https://github.com/PrismLibrary/Prism 它不再积极开发 2015-10-30:新版Prism:https://github.com/PrismLibrary/Prism 直接使用它的类会导致样板和可读性较差的代码

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

【讨论】:

不再开发棱镜是完全错误的。 是的,现在错了(不久前发布的新版 Prism)。当我回答这个问题时,Prism 的未来相当模糊。但是 IEventAggregator 仍然缺乏可测试性(我的回答仍然相关)。 请注意,Prism 有一个BindableBase VM 基类,它提供了一个SetProperty() 方法来处理RaisePropertyChanged() 样板,即:名称的设置器将是:set SetProperty(ref _name, value); 。这使得它几乎与 MVVM Light VM 相同【参考方案2】:

我不相信 MS 曾经将 PRISM 推广为“框架”,就像 MVVM Light、Caliburn 等被“宣传”一样。我的理解是,PRISM 总是作为一种“实践”呈现给“世界”。我相信,这仅仅意味着构建应用程序的有组织的方式。由于 PRISM 提供的所有代码,它变得有点令人困惑,人们可以将其视为可用于构建应用程序的“框架”。而且,确实,您可以使用 PRISM 提供的代码来构建您自己的应用程序。但是,在我看来,PRISM 比可用于 MVVM 的“框架”复杂得多,而且学习曲线陡峭,并且可能会“过度杀伤”并使您的应用程序变得比必要的复杂。如果您在构建应用程序时有时间学习最新的“框架”或“实践”,那就太好了!我的经验是,我没有机会学习一些新的“实践”或最新的“框架”,但必须完成工作。在一个理想的世界里,人们希望能够使用最新最好的“框架”或采用最新的“实践”,但有时你只需要坚持你已经知道的并完成它。

【讨论】:

嗨,欢迎来到堆栈溢出。我不确定这是否“回答”了这里的“问题”(也......过度使用“吓人的引号”会使这更难“阅读”;))。原始海报提出了三个非常具体的问题,您的回复中没有解决这些问题。也许你也可以解决这些问题?否则,我会将您在上面写的内容归类为评论而不是答案……这意味着它更正确地放在评论部分(就在原始问题的下方)。现在 - 你还没有足够的代表来发表评论......但如果答案只是评论,它会被评论者迅速删除。 @TarynEast 尚未被删除 =) 你想要我吗?我能。 ;) 注意:您现在有足够的代表将其移至 cmets 部分...【参考方案3】:

您无法完全比较 Prism 和 MvvmLight。

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

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

MVVM 只是 prism 提供解决问题的另一个方面。

这就像比较 Angular 和 Knockout。 AngularJS 管理整个应用程序并定义应用程序代码的结构指南,而使用 KnockoutJS,应用程序结构完全取决于您。 Prism 和 MvvmLight 的情况类似。

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

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

请观看有关使用 Prism 简化 MVVM 的网络研讨会。主持人是布赖恩·拉古纳斯: https://www.youtube.com/watch?v=ZfBy2nfykqY

【讨论】:

以上是关于wpf的棱镜vs mvvm灯的主要内容,如果未能解决你的问题,请参考以下文章

带棱镜的 mvvm:从菜单项设置视图

使用带有棱镜的 MVVM 在视图之间切换

自定义 MVVM 实现与。棱镜

WPF 的棱镜是啥?

我应该使用什么样的MVVM框架? [关闭]

WPF 从属性赋值到MVVM模式详解