Log In
Home
Support
Demos
Documentation
Blogs
Training
Webinars
[Expand]General Information
[Collapse]WinForms Controls
  Prerequisites
 [Expand]What's Installed
 [Collapse]Build an Application
  [Expand]Choose Application UI
   Data Management Controls
  [Expand]Printing and Exporting
  [Collapse]WinForms MVVM
   [Collapse]Concepts
     Conventions and Attributes
     Fluent API Support
     Data Bindings and Notifications
     Commands
     Services
     Behaviors
     Layer Communication. Messenger
     View Management
   [Expand]Design-time Support
   [Expand]Tutorials
  [Expand]Skins
  [Expand]Localization
  [Expand]Right-to-Left Layout
   Redistribution and Deployment
 [Expand]Controls and Libraries
 [Expand]Common Features
  Get More Help
 [Expand]API Reference
[Expand]ASP.NET Controls and MVC Extensions
[Expand]ASP.NET Bootstrap Controls
[Expand]WPF Controls
[Expand]Xamarin Controls
[Expand]Windows 10 App Controls
[Expand]Document Server
[Expand]Reporting
[Expand]Report Server
[Expand]Dashboard
[Expand]eXpressApp Framework
[Expand]CodeRush
[Expand]Cross-Platform Core Libraries
[Expand]Tools and Utilities
 End-User Documentation

Services

Consider trivial task like showing a notification (e.g., a message box) from a ViewModel. As a visual element, any message box is in fact, part of a View. Thus, if you show your message box directly from a ViewModel (define a command that calls the MessageBox.Show() method), this simple code will break the main MVVM concept - ViewModels must not refer to Views - and make it impossible to write unit-tests for your ViewModel. To get around this difficulty, the DevExpress MVVM Framework implements Services.

A service is a kind of IOC pattern that removes any references between a ViewModel and View layers. In code, a service is an interface used within the ViewModel code without any assumption of "when" and "how" this interface is implemented.

You can implement your own custom services as well as use DevExpress services. Whatever service you are using, the common workflow remains the same:

  • define a service in code (skipped if you're using the service already implemented by DevExpress);
  • register it in a specific View;
  • retrieve the service in a ViewModel and use its methods.

Expanded DevExpress Services

MVVM Best Practices Demo

The text below has a related example in the DevExpress 'MVVM Best Practices' demo.

Group: API Code Examples
Module: Services
Example: All examples except for the 'Custom Services'
17.1 Demo Center: Launch the demo

The DevExpress MVVM Framework already has ready-to-use services for most common tasks - displaying messages, flyouts, dialogs, adding Application UI Manager documents etc. For instance, the following ViewModel code retrieves the XtraMessageBoxService by defining a property of the IMessageBoxService type.

For POCO ViewModels you can use the following fail-safe syntax, that will automatically use the this.GetService method or raise an exception if something goes wrong.

After the service is retrieved, you can use its methods in your ViewModel:

Lastly, register your service within the View. Services are registered in either a local container for use within the individual View (local services), or in a global static (singleton) service container that allows you to use registered services across the entire application (global services).

Services can also be registered in service containers at runtime, when a ViewModel is created.

Finally, you can override the parent's service implementation at any level of the ViewModel's hierarchy by providing a custom service implementation at this level.

With the MvvmContext component you do not need to remember this underlying service container mechanic. The component's API provides easy-to-use methods to register services on both global and local levels.

Many ready-to-use services are already registered within the global static container, so you do not even need to register them manually. Remove the RegisterMessageBoxService method call within the MessageBox Service demo and you will notice that the service is still working.

You can re-define these service registrations, if needed. To do so, use the corresponding static Register... methods of the MVVMContext class. For example, ViewModels of both XtraMessageBox Service and FlyoutMessageBox Service examples are same as the first example's ViewModel. All three ViewModels retrieve a service that implements the IMessageBoxService. However, using different Register... static methods forces different services to be used.

The same approach allows examples from the Dialog Services group to display different dialogs, although the ViewModel code is identical.

Different dialogs are called due to the View code that registers different services.

Expanded Custom Services

MVVM Best Practices Demo

The text below has a related example in the DevExpress 'MVVM Best Practices' demo.

Group: API Code Examples
Module: Commanding
Example: Custom Services
17.1 Demo Center: Launch the demo

For custom services, you first need to implement this service somewhere in a separate class. For instance, the application has the custom interface IMyNotificationService with the Notify method.

Then, a custom service CustomService1 that implements this interface will look like the following.

As a variation, create another service that implements the same interface, but uses the different method overload.

Properties that retrieve your custom services in the ViewModel code will look like the following.

And here's the DoSomething method that can be bound to a UI element (e.g., a button). It will display two messages with the same text.

Finally, register your custom services in a View. Since these are your own custom services, no pre-defined static MVVMContext methods to register these services exist. Instead, call the local MvvmContext instance's RegisterService method.

So, you've passed all the necessary steps: implemented your custom services, used the ViewModel to retrieve them, used their methods and registered them within your View. However, if you try your application at runtime, both message boxes will have the 'Service2' caption. This means both Notify methods called are methods of the CustomService2 class.

Tip

Upon registration, services take a specific place in a hierarchical tree. Whenever the framework needs a service, it starts seeking from the bottom of the tree, moving up until an appropriate service is found. It was mentioned earlier that many ready-to-use services are already registered in the static container. These services sit on the very top of the hierarchy and are used if the framework has not found any custom services on lower levels. If neither default service exists, an exception occurs. In this example, both custom services are registered on the same hierarchy level. Since both services implement the same IMyNotificationService service, they are both considered as appropriate services when the Notify method of either Service or AnotherService objects is called. But the CustomService2 was the last to be registered, so it sits closer to the hierarchy bottom and is always 'found' first by the framework. You can trick this mechanism and register the CustomService2 using the RegisterDefaultService method instead. This will register your CustomService2 in the static container at the top of the hierarchy and make the CustomSerive1 the lowermost service. After that, the framework will always pick the CustomService1.

To overcome this issue, you can define service keys. A key is a string identifier that marks specific services. For POCO ViewModels you can set a service key as a parameter of the [ServiceProperty] attribute.

For non-POCO ViewModels, a service key can be set as a parameter of the GetService extension method.

Now, you have to register your custom services with these unique keys. All Register methods have corresponding overloads to do that.

As a result, when you call the Notify methods, the framework will not be confused about which IMyNotificationService service implementation should be used. Instead, it will take the exact service marked with your custom key. For instance, the AnotherService property will seek for the service marked with the 'Service2' key and will find the registered CustomService2 service. The same with the Service property, which will use the CustomService1 service because it was marked with the 'Service1' key.

If you test the application now, you will see that both message boxes now display different captions, since they are shown by methods of different services.

How would you rate this topic?​​​​​​​