6.【DevExpress MVVM】服务

DevExpress MVVM框架提供了一系列内置服务,如消息框和对话框,以遵循MVVM模式。服务允许ViewModel通过接口调用,而不直接引用视图。为了实现解耦,开发者可以自定义服务并在ViewModel中使用,同时在视图中注册。自定义服务的实现包括定义接口、创建服务类、在ViewModel中声明属性并注册服务。服务注册的关键在于服务定位,使用服务键可以避免服务冲突。
摘要由CSDN通过智能技术生成

目录

1. DevExpress提供的服务

2. 自定义服务


虑一些琐碎的任务,比如从ViewModel中显示通知(例如消息框)。作为一个视觉元素,任何消息框实际上都是视图的一部分。因此,如果你直接从ViewModel中显示消息框(定义调用MessageBox.Show()方法的命令),这段简单的代码破坏了MVVM最主要的原则——ViewModels不能引用视图——并使您不可能为ViewModel编写单元测试。为了解决这个问题,DevExpress MVVM框架实现了服务。

服务是一种IOC模式,它干掉了ViewModel层和View层之间的所有关系。在代码中,服务仅仅是ViewModel代码中的一个接口,它不需要关心接口在何时实现,又是如何实现的。

你可以实现自己的自定义服务,也可以使用DevExpress提供的服务。无论你使用的是什么服务,通常它的工作流程都是相同的:

  1. 在代码中定义一个服务(如果您使用的服务已经实现了DevExpress跳过)
  2. 在指定的View中注册它
  3. 在ViewModel中检索服务并使用其方法。

1. DevExpress提供的服务

DevExpress MVVM框架已经为最常见的任务提供了现成的服务——显示消息、弹出窗口、对话框、添加Application UI Manager的文档等等。例如,下面的ViewModel代码通过定义IMessageBoxService类型的属性来检索XtraMessageBoxService

//ViewModel
public class MyViewModel {
    protected IMessageBoxService MessageBoxService {
        get { return this.GetService<IMessageBoxService>(); }
    }
}

重要:
GetService方法不是线程安全的,不应该从后台线程调用。

对于POCO ViewModel,可以使用以下fail-safe(自动防故障)语法,该语法将自动使用this.GetService方法或在出错时引发异常。

//POCO ViewModel
protected virtual IMessageBoxService MessageBoxService {
    get { throw new System.NotImplementedException(); }
}

服务被检索到之后,你可以在ViewModel中使用它的方法:

public void SayHello() {
    MessageBoxService.Show("Hello!");
}

最后,在View中注册你的服务。服务注册在本地容器中以供在单个视图中使用(本地服务),或者注册在全局静态(单例)服务容器中,后者允许您在整个应用程序中使用注册的服务(全局服务)。

//全局service
DevExpress.Mvvm.ServiceContainer.Default.RegisterService(new SomeService());
//本地service
serviceContainer.RegisterService(new SomeFilterService(ModuleType.MyCustomFilter));

服务也可以在运行时在服务容器中注册,不过需要当创建ViewModel时候。

this.ServiceContainer.RegisterService(new Services.AnotherService());

最后,你可以在ViewModel的层次结构的任意级别上覆盖父级的服务实现,方法是在这个级别上提供自定义服务实现。

serviceContainer.RegisterService(new NotImplementedCustomService(ModuleType.MyMainView));

使用MvvmContext组件,你不需要记住这个底层服务容器机制。组件的API提供了易于使用的方法来注册全局和本地级别的服务。

//静态方法,用于注册全局的DevExpress XtraDialogService
MVVMContext.RegisterXtraDialogService();
//在默认服务容器(全局服务)中注册一个叫Service1的服务
mvvmContext1.RegisterDefaultService(new Service1());
//仅为当前View中使用而注册的一个叫Service2的本地服务
mvvmContext1.RegisterService(new Service2());

许多现成的服务已经在全局静态容器中注册,因此您甚至不需要手动注册它们。删除MessageBox服务演示中的RegisterMessageBoxService方法调用,你将注意到该服务仍在工作。

如果有必要,你可以重新定义这些服务注册。为此,你需要使用相应的MVVMContext类中的静态Register... 方法。例如,XtraMessageBox 服务和FlyoutMessageBox 服务例子的ViewModel与第一个例子的ViewModel相同。这三个ViewModel都检索实现IMessageBoxService的服务。然而,使用不同的静态 Register...  方法强制使用不同的服务。
同样的方法允许来自Dialog Services 分组的例子显示不同的对话框,尽管ViewModel代码是相同的。

protected IDialogService DialogService {
    get { return this.GetService<IDialogService>(); }
}

不同的对话框会被调用,是因为注册了不同服务的View代码。

//XtraDialog service
MVVMContext.RegisterXtraDialogService();
//FlyoutDialog service
MVVMContext.RegisterFlyoutDialogService();

2. 自定义服务

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

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

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

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

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

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

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

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

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

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

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

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

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

因此,您已经完成了所有必要的步骤:①实现自定义服务、②使用ViewModel检索它们、③使用它们的方法并在视图中注册它们。但是,如果您在运行时尝试您的应用程序,两个消息框将具有'Service2'标题。这意味着两个被调用的Notify方法都是CustomService2类的方法。

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

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

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

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

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

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

因此,当您调用Notify方法时,框架将不会混淆应该使用哪个IMyNotificationService服务实现。相反,它将接受用您的自定义key标记的确切服务。例如,AnotherService属性将查找标有Service2 key的服务,并查找已注册的CustomService2服务。服务属性也一样,它将使用CustomService1服务,因为它是用 Service1 key标记的。
如果您现在测试应用程序,您将看到两个消息框现在显示不同的标题,因为它们是由不同服务的方法显示的。

【DevExpress MVVM】中文翻译系列.文章目录

DevExpress.WindowsForms.v20.1.chm离线英文原版文档下载

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值