1. Prism 简介

Prism 是一个用于构建松耦合、可维护和可测试的 XAML 应用的框架,它支持所有还活着的基于 XAML 的平台,包括 WPF、Xamarin Forms、WinUI 和 Uwp Uno。Prism 提供了一组设计模式的实现,这些模式有助于编写结构良好且可维护的 XAML 应用程序,包括 MVVM、依赖项注入、命令、事件聚合器等。

Prism 是一个有10年以上历史的框架,而上个月才刚发布了它的 8.0 版本,这意味着现在网上能找到的大部分 Prism 的资料都已经有点过时,连 官方文档 也不例外。如果你需要详细的文档,除了官方文档,我会推荐 RyzenAdorer 的 Prism 系列文章:

NET Core 3 WPF MVVM框架 Prism系列文章索引 - RyzenAdorer -

如果你不需要那么详细的文档,只需要一个入门的教程,那么我希望我写的这两篇文章可以帮到你。

2. Prism.Core、Prism.Wpf 和 Prism.Unity

从很久以前开始,臃肿 就是 Prism 被提起最多的标签。毕竟比起 MVVMLight,Prism 实现的功能更多;对于初学者来说,刚打开 Prism 的文档很可能会马上选择放弃。Prism 的文档详细到让人望而却步,例如多年前的旧版官方文档的 其中一篇

不是 6 分钟,不是 16 分账,是整整 60 分钟,Prism 的旧文档随便打开一篇都吓死人。而 Prism 的各种包更是多到离谱。例如几年前的 Prism 6.3,其中 WPF 平台的项目有这么多个:

  • Prism.Wpf
  • Prism.Autofac
  • Prism.DryIoc
  • Prism.Mef
  • Prism.Ninject
  • Prism.StructureMap
  • Prism.Unity

所以臃肿是很多人对 Prism 的印象。

减肥是一个永恒的受欢迎的话题,对 Prism 也是一样。相比 Prism 6.3,刚刚发布的 8.0 已经好很多了(虽然还是有很多个项目),例如 WPF 平台的项目已经大幅删减,只保留了 Prism.Wpf、Prism.DryIoc 和 Prism.Unity,也就是说现在 Prism 只支持 DryIoc 和 Unity 两种 IOC 容器。这样一来 Prism 项目的结构就很清晰了。

以 WPF 为例,核心的项目是 Prism.Core,它提供实现 MVVM 模式的核心功能以及部分各平台公用的类。然后是 Prism.Wpf,它提供针对 Wpf 平台的功能,包括导航、弹框等。最后由 Prism.Unity 指定 Unity 作为 IOC 容器。

即使已精简了这么多,Prism 还是有很多功能,两篇文章也不足以讲解全部内容,所以我只会介绍最常用到的入门知识。这篇文章首先介绍 Prism.Core 的主要功能。

3. Prism.Core

Prism.Core 可以单独安装,目前最新的版本是 8.0.0.1909:

Install-Package Prism.Core -Version 8.0.0.1909

除了一些各个平台都用到的零零碎碎的公用类,作为一个 MVVM 库 Prism.Core 主要提供了下面三方面的功能:

  • BindableBase 和 ErrorsContainer
  • Commanding
  • Event Aggregator

这些功能已经覆盖了 MVVM 的核心功能,如果只需要与具体平台无关的 MVVM 功能,可以只安装 Prism.Core。

4. BindableBase 和 ErrorsContainer

数据绑定是 MVVM 的核心元素之一,为了使绑定的数据可以和 UI 交互,数据类型必须继承 INotifyPropertyChangedBindableBase 实现了 INotifyPropertyChanged 最简单的封装,它的使用如下:

public class MockViewModel : BindableBase
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set { SetProperty(ref _myProperty, value); }
}
}

其中 SetProperty 判断 _myProperty 和 value 是否相等,如果不相等就为 _myProperty 赋值并触发 OnPropertyChanged 事件。

除了 INotifyPropertyChanged,绑定机制中另一个十分有用的接口是 INotifyDataErrorInfo,它用于公开数据验证的结果。Prism 提供了 ErrorsContainer 以便管理及通知数据验证的错误信息。要使用 ErrorsContainer,可以先写一个类似这样的基类:

public class DomainObject : BindableBase, INotifyDataErrorInfo
{
public ErrorsContainer<string> _errorsContainer; protected ErrorsContainer<string> ErrorsContainer
{
get
{
if (_errorsContainer == null)
_errorsContainer = new ErrorsContainer<string>(s => OnErrorsChanged(s)); return _errorsContainer;
}
} public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public void OnErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
} public IEnumerable GetErrors(string propertyName)
{
return ErrorsContainer.GetErrors(propertyName);
} public bool HasErrors
{
get { return ErrorsContainer.HasErrors; }
}
}

然后就可以在派生类中通过 ErrorsContainer.SetErrorsErrorsContainer.ClearErrors 管理数据验证的错误信息:

public class MockValidatingViewModel : DomainObject
{
private int mockProperty; public int MockProperty
{
get
{
return mockProperty;
} set
{
SetProperty(ref mockProperty, value); if (mockProperty < 0)
ErrorsContainer.SetErrors(() => MockProperty, new string[] { "value cannot be less than 0" });
else
ErrorsContainer.ClearErrors(() => MockProperty);
}
}
}

5. Commanding

ICommand 同样是 MVVM 模式的核心元素,DelegateCommand 实现了 ICommand 接口,它最基本的使用形式如下,其中 DelegateCommand 构造函数中的第二个参数 canExecuteMethod 是可选的:

public DelegateCommand SubmitCommand { get; private set; }

public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand(Submit, CanSubmit);
} private void Submit()
{
//implement logic
} private bool CanSubmit()
{
return true;
}

另外它还有泛型的版本:

public DelegateCommand<string> SubmitCommand { get; private set; }

public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand<string>(Submit, CanSubmit);
} private void Submit(string parameter)
{
//implement logic
} private bool CanSubmit(string parameter)
{
return true;
}

通常 UI 会根据 ICommandCanExecute 函数的返回值来判断触发此 Command 的 UI 元素是否可用。CanExecute 返回 DelegateCommand 构造函数中的第二个参数 canExecuteMethod 的返回值。如果不传入这个参数,则 CanExecute 一直返回 True。

如果 CanExecute 的返回值有变化,可以调用 RaiseCanExecuteChanged 函数,它会触发 CanExecuteChanged 事件并通知 UI 元素重新判断绑定的 ICommand 是否可用。除了主动调用 RaiseCanExecuteChangedDelegateCommand 还可以用 ObservesPropertyObservesCanExecute 两种形式监视属性,定于属性的 PropertyChanged 事件并改变 CanExecute

private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set { SetProperty(ref _isEnabled, value); }
} private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set { SetProperty(ref _canSave, value); }
} public CheckUserViewModel()
{
SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);
//也可以写成串联方式
SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled).ObservesProperty<bool>(() => CanSave); SubmitCommand = new DelegateCommand(Submit).ObservesCanExecute(() => IsEnabled);
}

6. Event Aggregator

本来Event Aggregator(事件聚合器)或 Messenger 之类的组件本来并不是 MVVM 的一部分,不过现在也成了 MVVM 框架的一个重要元素。解耦是 MVVM 的一个重要目标,'EventAggregator' 则是实现解耦的重要工具。在 MVVM 中,对于 View 和与他匹配的 ViewModel 之间的交互,可以使用 INotifyPropertyIcommand;而对于必须通信的不同 ViewModel 或模块,为了使它们之间实现低耦合,可以使用 Prism 中的 EventAggregator。如下图所示,Publisher 和 Scbscriber 之间没有直接关联,它们通过 Event Aggregator 获取 PubSubEvent 并发送及接收消息:

要使用 EventAggregator,首先需要定义 PubSubEvent

public class TickerSymbolSelectedEvent : PubSubEvent<string>{}

发布方和订阅方都通过 EventAggregator 索取 PubSubEvent,在 ViewModel中通常都是通过依赖注入获取一个 IEventAggregator

public class MainPageViewModel
{
IEventAggregator _eventAggregator;
public MainPageViewModel(IEventAggregator ea)
{
_eventAggregator = ea;
}
}

发送方的操作很简单,只需要 通过 GetEvent 拿到 PubSubEvent,把消息发布出去,然后拍拍屁股走人,其它的责任都不用管:

_eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");

订阅方是真正使用这些消息并负责任的人,下面是最简单的通过 Subscribe 订阅事件的代码:

public class MainPageViewModel
{
public MainPageViewModel(IEventAggregator ea)
{
ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);
} void ShowNews(string companySymbol)
{
//implement logic
}
}

除了基本的调用方式,Subscribe 函数还有其它可选的参数:

public virtual SubscriptionToken Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)

其中 threadOption 指示收到消息后在哪个线程上执行第一个参数定义的 action,它有三个选项:

  • PublisherThread,和发布者保持在同一个线程上执行。
  • UIThread,在 UI 线程上执行。
  • BackgroundThread,在后台线程上执行。

第三个参数 keepSubscriberReferenceAlive 默认为 false,它指示该订阅是否为强引用。

  • 设置为 false 时,引用为弱引用,用完可以不用管。
  • 设置为 true 时,引用为强引用,用完需要使用 Unsubscribe 取消订阅。

下面代码是一段订阅及取消订阅的示例:

public class MainPageViewModel
{
TickerSymbolSelectedEvent _event; public MainPageViewModel(IEventAggregator ea)
{
_event = ea.GetEvent<TickerSymbolSelectedEvent>();
_event.Subscribe(ShowNews);
} void Unsubscribe()
{
_event.Unsubscribe(ShowNews);
} void ShowNews(string companySymbol)
{
//implement logic
}
}

7. 生产力工具

如果觉得属性和 DelegateCommand 的定义有些啰嗦,可以试试安装这个工具:Prism Template Pack,它提供了一些实用的代码段和一些 Project 和 Item 的模板。例如,安装此工具后可以通过 cmd 代码段快速生成一个完整的 DelegateCommand 代码:

private DelegateCommand _fieldName;
public DelegateCommand CommandName =>
_fieldName ?? (_fieldName = new DelegateCommand(ExecuteCommandName)); void ExecuteCommandName()
{ }

更多代码段定义请参考官方文档:Productivity Tools Prism

8. 结语

Prism.Core 最初由 Microsoft Patterns and Practices 团队创建,现在转移到社区。虽然 Prism 框架非常成熟(还有点臃肿),支持插件和定位控件的区域,但 Prism.Core 很轻,仅包含几个常用的类型。这篇文章已经把 Prism.Core 中最常用的类尽可能简单地介绍过一遍,这足够用完创建一个基于 MVVM 框架的项目。

Prism 的更多功能将在下一篇文章中介绍。

9. 参考

https://github.com/PrismLibrary/Prism

https://prismlibrary.com/docs/index.html

INotifyPropertyChanged 接口

INotifyDataErrorInfo 接口

ICommand 接口

[Windows] Prism 8.0 入门(上):Prism.Core的更多相关文章

  1. [Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unity

    1. Prism.Wpf 和 Prism.Unity 这篇是 Prism 8.0 入门的第二篇文章,上一篇介绍了 Prism.Core,这篇文章主要介绍 Prism.Wpf 和 Prism.Unity ...

  2. ASP.NET Core 1.0 入门——了解一个空项目

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  3. net core体系-web应用程序-4asp.net core2.0 项目实战(CMS)-第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了   原文链接:https://www.cnblogs.com/yilezhu/p/9985451.ht ...

  4. 《Prism 5.0源码走读》Prism 5.0简介

    Prism是一个开发和设计模块化WPF应用的基础框架,里面包含了MVVM pattern和设计示例.当前最新的版本是Prism 5.0,官方网站:https://compositewpf.codepl ...

  5. 推荐:【视频教程】ASP.NET Core 3.0 入门

    墙裂推荐了,免费,通俗易懂,唯一可惜的就是不是我录的,更可惜的是人家录制完了快半年了我还没看完... 版权归原作者所有,建议新手还是边看边实践吧,要不然过完一遍发现自己啥也没学会,不要眼高手低 [视频 ...

  6. ASP.NET Core 3.0 入门

    原文:ASP.NET Core 3.0 入门 课程简介 与2.x相比发生的一些变化,项目结构.Blazor.SignalR.gRPC等 课程预计结构 ASP.NET Core 3.0项目架构简介 AS ...

  7. GraphPad Prism 9.0安装破解教程

    graphpad prism 9.0是一款强大的科学软件,拥有大量分析图表,prism是回归分析的著名软件之一,非常适用于科研生物医学等领域.本文提供其破解版,激活码,序列号,破解教程等,可以完美激活 ...

  8. windows server2012R2 上 .net core IIS 部署--应用程序池 自动停止

    在windows server2016安装部署.NET CORE时,只需要将.net core应用程序池设置无托管,然后对应你项目的版本安装一个dotnet-hosting-2.2.6-win.exe ...

  9. ASP.NET Core 1.0 入门——Application Startup

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

随机推荐

  1. 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)

    再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...

  2. JSP启动,错误500,实例化Servlet类异常

    jps的页面报 HTTP   500  -Internal Server Error 实例化Servlet类[servlet.Userservler]异常 javax.servlet.ServletE ...

  3. 前言「HarmonyOS应用开发基础篇」

    场景一.随着智能设备种类的不断增多,我们基本上每人都有好几台智能设备,比如智能手机,平板,耳机,音响,穿戴设备等等.这些设备都具有独立性,偶尔的组合也是我们通过手动去搭配,并且不一定能够完全组合在一起 ...

  4. kudu集群:kudu_master、kudu_tserver服务及数据的迁移(根据官网总结)

    是不是都需要一个声明,来一个: 声明: 本文只是总结本人本地模拟环境测试,并没有经过严格的线上测试.请自己在本地严格测试之后慎重使用在生产环境! kudu_master.kudu_tserver服务迁 ...

  5. 使用arm-none-eabi-gdb报错error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

    使用arm-none-eabi-gdb报错error while loading shared libraries: libncurses.so.5: cannot open shared objec ...

  6. Spider--补充--Requests--session&cookie

    # session 与 cookie # 可能大家对session已经比较熟悉了,也大概了解了session的机制和原理,但是我们在做爬虫时如何会运用到session呢,就是接下来要讲到的会话保持. ...

  7. .Net核心依赖项注入:生命周期和最佳实践

    在讨论.Net的依赖注入(DI)之前,我们需要知道我们为什么需要使用依赖注入 依赖反转原理(DIP): DIP允许您将两个类解耦,否则它们会紧密耦合,这有助于提高可重用性和更好的可维护性 DIP介绍: ...

  8. http服务器文件名大小写忽略

    问题 文件从windows里面放到nginx里面去的时候,文件在windows下面是大小写忽略,也就是不论大小写都可以匹配的,而到linux下面的时候,因为linux是区分大小写的,也就是会出现无法忽 ...

  9. Linux内核剖析——操作系统的启动

    一.总体功能 1.从通电到BIOS跳转 1.1 CPU在通电后,先进入实模式,设置CS=0XFFFF,IP = 0X0000(指向BIOS) 1.2 BIOS进行执行系统监测,并且在地址=0处初始化中 ...

  10. div定时放大缩小

    <!DOCTYPE html> <html> <head> <style> .anim{ width: 100px; height: 100px; ba ...