DevExpress WinForm控件入门指南:WinForms MVVM – 服务(二)

考虑像显示来自 ViewModel 的通知(例如,消息框)这样的微不足道的任务,作为可视化元素,任何消息框实际上都是视图的一部分。 因此,如果你直接从 ViewModel 显示消息框(定义一个调用 MessageBox.Show() 方法的命令),这个简单的代码将破坏主要的MVVM概念 – ViewModels不能引用Views,并使其无法编写ViewModel的单元测试。为了解决这个困难,DevExpress MVVM 框架实现了服务。

服务是一种 IOC 模式,它删除了 ViewModel 和 View 层之间的任何引用。 在代码中,服务是在 ViewModel 代码中使用的接口,没有任何关于“何时”和“如何”实现该接口的假设。

您可以实现自己的自定义服务以及使用 DevExpress Services,无论您使用什么服务,通用工作流程都保持不变:

  • 在代码中定义服务(如果您使用的是 DevExpress 已经实现的服务,则跳过);
  • 在特定的视图中注册它;
  • 在ViewModel中检索服务并使用其方法。
自定义服务

对于自定义服务,您首先需要在单独类中的某处实现此服务,例如应用程序具有带有 Notify 方法的自定义接口 IMyNotificationService。

C#

//Viewpublic interface IMyNotificationService {void Notify(string message);}

VB.NET

'ViewPublic Interface IMyNotificationServiceSub Notify(ByVal message As String)End Interface

然后,实现此接口的自定义服务 CustomService1 将如下所示。

C#

//Service1.csclass CustomService1 : IMyNotificationService {void IMyNotificationService.Notify(string message) {System.Windows.Forms.MessageBox.Show(message, "Service1");}}

VB.NET

'Service1.vbFriend Class CustomService1Implements IMyNotificationServicePrivate Sub IMyNotificationService_Notify(ByVal message As String) Implements IMyNotificationService.NotifySystem.Windows.Forms.MessageBox.Show(message, "Service1")End SubEnd Class

作为变体,创建另一个实现相同接口但使用不同方法重载的服务。

C#

//Service2.csclass CustomService2 : IMyNotificationService {void IMyNotificationService.Notify(string message) {System.Windows.Forms.MessageBox.Show(message, "Service2");}}

VB.NET

'Service2.vbFriend Class CustomService2Implements IMyNotificationServicePrivate Sub IMyNotificationService_Notify(ByVal message As String) Implements IMyNotificationService.NotifySystem.Windows.Forms.MessageBox.Show(message, "Service2")End SubEnd Class

在 ViewModel 代码中检索自定义服务的属性将如下所示。

C#

//ViewModelpublic virtual IMyNotificationService Service {get { throw new NotImplementedException(); }}public virtual IMyNotificationService AnotherService {get { throw new NotImplementedException(); }}

VB.NET

'ViewModelPublic Overridable ReadOnly Property Service() As IMyNotificationServiceGetThrow New NotImplementedException()End GetEnd PropertyPublic Overridable ReadOnly Property AnotherService() As IMyNotificationServiceGetThrow New NotImplementedException()End GetEnd Property

这是可以绑定到 UI 元素(例如,按钮)的 DoSomething 方法,它将显示具有相同文本的两条消息。

C#

//ViewModelpublic void DoSomething() {Service.Notify("Hello");AnotherService.Notify("Hello");}

VB.NET

'ViewModelPublic Sub DoSomething()Service.Notify("Hello")AnotherService.Notify("Hello")End Sub

最后,在视图中注册您的自定义服务。 由于这些是您自己的自定义服务,因此不存在用于注册这些服务的预定义静态 MVVMContext 方法。 相反,调用本地 MvvmContext 实例的 RegisterService 方法。

C#

//ViewmvvmContext1.RegisterService(new CustomService1());mvvmContext1.RegisterService(new CustomService2());

VB.NET

'ViewmvvmContext1.RegisterService(New CustomService1())mvvmContext1.RegisterService(New CustomService2())

提示:注册后,服务在分层树中占据特定位置。每当框架需要服务时,它就会从树的底部开始寻找,向上移动直到找到合适的服务。前面有提到很多现成的服务已经在静态容器中注册了,这些服务位于层次结构的最顶层,如果框架没有在较低级别找到任何自定义服务,则会使用这些服务。如果这两个默认服务都不存在,则会发生异常。在此示例中,两个自定义服务都注册在同一层次结构级别上。由于这两个服务实现了相同的 IMyNotificationService 服务,因此在调用 Service 或 AnotherService 对象的 Notify 方法时,它们都被视为合适的服务。但是 CustomService2 是最后注册的,因此它更靠近层次结构底部,并且总是首先被框架“找到”。您可以欺骗此机制并使用 RegisterDefaultService 方法注册 CustomService2,这将在层次结构顶部的静态容器中注册您的 CustomService2,并使 CustomSerive1 成为最底层的服务。之后,框架将始终选择 CustomService1。

为了解决这个问题,您可以定义服务密钥。key是标记特定服务的字符串标识符,对于 POCO ViewModel,您可以将服务密钥设置为 [ServiceProperty] 属性的参数。

C#

[ServiceProperty(Key="Service1")]public virtual IMyNotificationService Service {get { throw new NotImplementedException(); }}[ServiceProperty(Key = "Service2")]public virtual IMyNotificationService AnotherService {get { throw new NotImplementedException(); }}

VB.NET

<ServiceProperty(Key:="Service1")>Public Overridable ReadOnly Property Service() As IMyNotificationServiceGetThrow New NotImplementedException()End GetEnd Property<ServiceProperty(Key := "Service2")>Public Overridable ReadOnly Property AnotherService() As IMyNotificationServiceGetThrow New NotImplementedException()End GetEnd Property

对于非 POCO ViewModel,可以将服务密钥设置为 GetService 扩展方法的参数。

C#

public IServiceInterface MyService {get { return this.GetService<IServiceInterface >("MyServiceKey"); }}

VB.NET

Public ReadOnly Property MyService() As IServiceInterfaceGetReturn Me.GetService(Of IServiceInterface )("MyServiceKey")End GetEnd Property

现在,您必须使用这些唯一密钥注册您的自定义服务,所有 Register 方法都有相应的重载来做到这一点。

C#

mvvmContext1.RegisterService("Service1", new CustomService1());mvvmContext1.RegisterService("Service2", new CustomService2());

VB.NET

mvvmContext1.RegisterService("Service1", New CustomService1())mvvmContext1.RegisterService("Service2", New CustomService2())

因此,当您调用 Notify 方法时,框架将不会混淆应该使用哪个 IMyNotificationService 服务实现。 相反它将采用标有您的自定义密钥的确切服务,例如,AnotherService 属性将寻找标有“Service2”键的服务,并找到已注册的 CustomService2 服务。 与 Service 属性相同,它将使用 CustomService1 服务,因为它标有“Service1”键。

如果您现在测试应用程序,您将看到两个消息框现在显示不同的标题,因为它们是由不同服务的方法显示的。

DevExpress WinForm | 下载试用

DevExpress WinForm拥有180+组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任!

更多产品正版授权详情及优惠,欢迎咨询在线客服>>


DevExpress技术交流群5:742234706      欢迎一起进群讨论

DevExpress年终放大招!省钱攻略提前奉上,更适合中国区用户!
标签:

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年11月27日
下一篇 2021年11月27日

相关推荐

发表回复

登录后才能评论