棱镜与mvvm光为wpf

Pad*_*aja 28 wpf prism mvvm mvvm-light

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

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

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

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

Vla*_*aev 20

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

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

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

  3. 使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

棱镜的缺点:

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

  • 棱镜不再发展是完全错误的. (3认同)

Ric*_*ves 7

你不能完全比较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


小智 5

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

  • 嗨,欢迎堆栈溢出。我不确定这是否在这里“回答”了“问题”(也...过多使用了“恐吓引号”,这使“阅读”更困难了;))。原始海报问了三个非常具体的问题,您的回答中没有解决。也许您也可以解决这些问题?否则,我会将您上面写的内容更多地归类为评论而不是答案……这意味着它更正确地出现在注释部分(仅在原始问题下方)。现在-您还没有足够的代表来评论...但是,如果答案仅是评论,则评论者会迅速将其删除。 (10认同)