Getting Started

  • 5 minutes to read

A Service is a special Behavior that implements an interface. Although Services are defined in Xaml, the Service interfaces can accessed from the View Model layer.

Assume that you need to show a message box from a View Model. The easiest way to accomplish this task is to use the MessageBox.Show method directly from the View Model. However, this approach breaks the main MVVM rule: the View Model layer should not refer to the View layer. Thus, this way makes it impossible to write unit tests for this View Model, because there is no one who can click the MessageBox button during a unit test. For solving such tasks in MVVM, use the Services mechanism.

Let's discuss how to solve the posed task with Services. We have the following View Model…

public class DocumentViewModel : ViewModelBase {
    public ICommand CloseDocumentCommand { get; private set; }
    public DocumentViewModel() {
        CloseDocumentCommand = new DelegateCommand(CloseDocument);
    }
    void CloseDocument() {
        MessageBoxResult canCloseDocument;
        //canCloseDocument = 
            //    MessageBox.Show("Want to save your changes?", " Document", MessageBoxButton.YesNoCancel);
        if(canCloseDocument == MessageBoxResult.Yes) {
            //...
        }
    }
}

… and the following View:

<UserControl x:Class="Example.View.DocumentView"
    xmlns:ViewModel="clr-namespace:Example.ViewModel" ...>
    <UserControl.DataContext>
        <ViewModel:DocumentViewModel/>
    </UserControl.DataContext>
    ...
        <Button Content="Close Document" Command="{Binding CloseDocumentCommand}" .../>
    ...
</UserControl>

The DevExpress.Xpf.Mvvm library provides the IMessageBoxService interface. Implementation of this interface is contained in the DevExpress.Xpf.Core library – the DXMessageBoxService class. To add this service to our View (DocumentView), add it to the Interaction.Behaviors collection as follows.

<UserControl x:Class="Example.View.DocumentView"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    xmlns:ViewModel="clr-namespace:Example.ViewModel" ...>
    <UserControl.DataContext>
        <ViewModel:DocumentViewModel/>
    </UserControl.DataContext>
    <dxmvvm:Interaction.Behaviors>
        <dx:DXMessageBoxService/>
    </dxmvvm:Interaction.Behaviors>
    ...
        <Button Content="Close Document" Command="{Binding CloseDocumentCommand}" .../>
    ...
</UserControl>
NOTE

Certain services that don't need to be associated with a specific control (like NotificationService, DispatcherService, etc) can be registered at the App.xaml level.

<Application.Resources>
  <dx:DXMessageBoxService x:Key="MessageBoxService"/>
</Application.Resources>

To access the application services from other than a view model, you can use the static Default property of the DevExpress.Mvvm.ServiceContainer class:

ServiceContainer.Default.GetService<IDispatcherService>();

Services are automatically injected to View Models, so they are available from there via an interface that is provided by a certain service.

As you may have noticed, our View Model (DocumentViewModel) is inherited from the ViewModelBase class. So, the DocumentViewModel supports the GetService<T> method that returns an interface used to access the DXMessageBoxService.

public class DocumentViewModel : ViewModelBase {
    public ICommand CloseDocumentCommand { get; private set; }
    public IMessageBoxService MessageBoxService { get { return GetService<IMessageBoxService>(); } }
    ...
    void CloseDocument() {
        MessageBoxResult canCloseDocument = MessageBoxService.Show(
            messageBoxText: "Want to save your changes?", 
            caption: "Document", 
            button: MessageBoxButton.YesNoCancel);
        if(canCloseDocument == MessageBoxResult.Yes) {
            //...
        }
    }
}

Thus, follow the steps below to use Services.

  • Set the View's DataContext to your View Model.
  • Define a service in XAML.
  • Get access to the service's interface from your View Model via the GetService<T> method.

The use of Services makes it easy to create unit-tests for your View Models. Let's write a test for the above-mentioned DocumentViewModel (Moq Framework is used).

[TestFixture]
public class DocumentViewModelTests {
    [Test]
    public void Test() {
        bool serviceIsCalled = false;
        var viewModel = new DocumentViewModel();
        var service = new Mock<IMessageBoxService>(MockBehavior.Strict);
        service.
           Setup(foo => foo.Show(
               "Want to save your changes?", "Document", MessageBoxButton.YesNoCancel, 
                MessageBoxImage.None, MessageBoxResult.None)).
           Returns((string text, string caption, MessageBoxButton button,
                MessageBoxImage image, MessageBoxResult none) => {
               serviceIsCalled = true;
               return MessageBoxResult.OK;
           });
        ((ISupportServices)viewModel).ServiceContainer.RegisterService(service.Object);
        viewModel.CloseDocumentCommand.Execute(null);
        Assert.IsTrue(serviceIsCalled);
    }
}
NOTE

You can download an example on how to use DXMessageBoxService from the following topic: DXMessageBoxService.

Services become available only when Views to which services are attached are loaded. To obtain services to perform preliminary actions, handle the View's Loaded event (for example, using EventToCommand) and access the required service there. Below is a code snippet that shows how to perform preliminary navigation using the FrameNavigationService.

<UserControl x:Class="DXSample.View.MainView" 
    ... 
    DataContext="{dxmvvm:ViewModelSource Type={x:Type ViewModel:MainViewModel}}">
    <Grid>
        <dxwui:NavigationFrame AnimationType="SlideHorizontal">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding OnViewLoadedCommand}" />
                <dxwuin:FrameNavigationService />
            </dxmvvm:Interaction.Behaviors>
        </dxwui:NavigationFrame>
    </Grid>
</UserControl>
public class MainViewModel {
    private INavigationService NavigationService { get { return this.GetService<INavigationService>(); } }

    public MainViewModel() {  }

    public void OnViewLoaded() {
        NavigationService.Navigate("HomeView", null, this);
    }
}