Getting Started with DevExpress MVVM Framework. Commands and View Models.

WPF Team Blog
29 August 2013

OTHER RELATED ARTICLES:

  1. THIS POST: Getting Started with DevExpress MVVM Framework. Commands and View Models.
  2. DevExpress MVVM Framework. Introduction to Services, DXMessageBoxService and DialogService.
  3. DevExpress MVVM Framework. Interaction of ViewModels. IDocumentManagerService.
  4. DevExpress MVVM Framework. Introduction to POCO ViewModels.
  5. DevExpress MVVM Framework. Interaction of ViewModels. Messenger.
  6. DevExpress MVVM Framework. Using Scaffolding Wizards for building Views.
  7. DevExpress MVVM Framework. Data validation. Implementing IDataErrorInfo.
  8. DevExpress MVVM Framework. Using DataAnnotation attributes and DevExpress Fluent API.
  9. DevExpress MVVM Framework. Behaviors.
  10. DevExpress MVVM Framework. TaskbarButtonService, ApplicationJumpListService and NotificationService.
  11. DevExpress MVVM Framework. Asynchronous Commands.
  12. DevExpress MVVM Framework. Converters.


Although the contents of this post remain valid, you can refer to another post to learn a new modern style for commanding and property binding in ViewModels: POCO ViewModels.

If you’ve developed a WPF or Silverlight application, you’ve likely used the MVVM pattern. You likely also encountered issues implementing some functionality under MVVM. Some mechanisms are difficult to implement without moving away from MVVM. What’s more, MVVM limitations may not relate to any particular control because the WPF/Silverlight platform itself has no full support for MVVM development. The typical problems with MVVM in WPF/Silverlight are well-known. In fact, several frameworks were specifically introduced to address these issues: PRISM, MVVM Light, Caliburn, etc. These frameworks can work in conjunction with DevExpress components as with standard components.

If several solutions already exist, why would DevExpress offer yet another MVVM framework?
The answer is, the better part of our customers use the MVVM pattern (including third party MVVM frameworks); we have a clear picture of the challenging scenarios in MVVM development. We can address issues solely within our components, but sometimes issues are related to the MVVM framework.
With an MVVM library of our own, MVVM capabilities are exposed at the component level.The end-result is a holistic solution for a well-designed MVVM application whose parts fit together perfectly.

In this series of posts we’ll discuss the following features provided by DevExpress MVVM Framework:

  1. Commands
  2. Basic ViewModel classes
  3. Attached behaviors
  4. Services
  5. Value converters
  6. Messaging and loosely-coupled MVVM architecture

The MVVM functionality at the DevExpress.Xpf.Mvvm library has no external dependencies, so you can use it in the view model part of your project without referencing a UI library.

 

Let’s review the commanding mechanism and ViewModel types. While you may find this subject familiar, the details are specific to our framework.

The MVVM library provides two implementations of the ICommand interface:

  1. DelegateCommand defining a parameterless command;
  2. DelegateCommand<T> defining a single parameter command of the parametrized type.

Both commands support two constructors: a constructor accepting Execute delegate; a second constructor accepting Execute and CanExecute delegates:

   1: DelegateCommand delegateCommand = 
   2:     new DelegateCommand(() => MessageBox.Show("This is a DelegateCommand"));
   3: DelegateCommand<string> delegateCommand = 
   4:     new DelegateCommand<string>(x => MessageBox.Show(x), x => !string.IsNullOrEmpty(x));

A DelegateCommand<T> automatically converts the command argument to the parameterized type if possible. For example, a CommandParameter string is converted to the parametrized type:

   1: <Button Command="{Binding ShowDocumentCommand}" CommandParameter="Text"/>
 
   1: public enum DocumentType { Text, Data }
   2: DelegateCommand<DocumentType> ShowDocumentCommand = 
   3:     new DelegateCommand<DocumentType>(OnShowDocumentCommandExecute);
   4:  
   5: void OnShowDocumentCommandExecute(DocumentType parameter) {
   6:     if(parameter == DocumentType.Text) {
   7:         ///
   8:     }
   9:     if(parameter == DocumentType.Data) {
  10:         ///
  11:     }
  12: }

An optional constructor parameter (for WPF only) specifies whether a DelegateCommand uses the CommandManager to raise the CanExecuteChanged event. By default useCommandManager is true, making it unnecessary to manually implement disabled/enabled logic for your commands. You can just set the CanExecute delegate for the command and the delegate is automatically triggered when an end-user interacts with the UI. Note that when the CommandManager is used for this purpose, the CanExecute handler is called frequently, so avoid performing time-consuming operations in the delegate.

If you pass false as the last constructor argument, the CommandManager is not used. In this case, update your command by calling the RaiseCanExecuteChanged method.

   1: DelegateCommand GoBackCommand = 
   2:     new DelegateCommand(OnGoBackCommandExecute, OnGoBackCommandCanExecute, false);
   3: DelegateCommand GoForwardCommand = 
   4:     new DelegateCommand(OnGoForwardCommandExecute, OnGoForwardCommandCanExecute, false);
   5: void OnGoBackCommandExecute() {
   6:     ///
   7:     UpdateCommandsState();
   8: }
   9: void OnGoForwardCommandExecute() {
  10:     ///
  11:     UpdateCommandsState();
  12: }
  13: bool OnGoBackCommandCanExecute() {
  14:     ///
  15: }
  16: bool OnGoForwardCommandCanExecute() {
  17:     ///
  18: }
  19: void UpdateCommandsState() {
  20:     GoBackCommand.RaiseCanExecuteChanged();
  21:     GoForwardCommand.RaiseCanExecuteChanged();
  22: }

A DelegateCommands can process an event in the view layer using the EventToCommand class. The following is a simple example binding a command to an event:

   1: <Window ...>
   2:     <dxmvvm:Interaction.Triggers>
   3:         <dxmvvm:EventToCommand Command="{Binding InitializeCommand}" EventName="Loaded"/>
   4:     </dxmvvm:Interaction.Triggers>
   5:     ...
   6: </Window>

The EventToCommand class allows passing event arguments as a command argument. It’s importantly to note in most cases event arguments are a part of the View layer, and it’s necessary to convert event arguments to an object that belonging to the ViewModel. EventToCommand provides an EventArgsConverter property for this purpose.

   1: <ListBox ...>
   2:     <dxmvvm:Interaction.Triggers>
   3:         <dxmvvm:EventToCommand EventName="MouseDoubleClick" 
   4:                                Command="{Binding ItemDoubleClickCommand}" 
   5:                                PassEventArgsToCommand="True">
   6:             <dxmvvm:EventToCommand.EventArgsConverter>
   7:                 <Helpers:ListBoxDoubleClickEventArgsConverter/>
   8:             </dxmvvm:EventToCommand.EventArgsConverter>
   9:         </dxmvvm:EventToCommand>
  10:     </dxmvvm:Interaction.Triggers>
  11: </ListBox>
 
   1: public class ListBoxDoubleClickEventArgsConverter : EventArgsConverterBase<MouseButtonEventArgs> {
   2:     protected override object Convert(MouseButtonEventArgs args) {
   3:         ///
   4:     }
   5: }

Some DevExpress components provide ready-to-use event argument converters. For instance, the EventArgsToDataRowConverter and EventArgsToDataCellConverter converters can be used with the GridControl. These converters pass a data row object or information about a cell to a command.

   1: <dxg:GridControl ... >
   2:     <dxmvvm:Interaction.Triggers>
   3:         <dxmvvm:EventToCommand Command="{Binding EditCommand}" 
   4:                                EventName="MouseDoubleClick" 
   5:                                PassEventArgsToCommand="True">
   6:             <dxmvvm:EventToCommand.EventArgsConverter>
   7:                 <dx:EventArgsToDataRowConverter />
   8:             </dxmvvm:EventToCommand.EventArgsConverter>
   9:         </dxmvvm:EventToCommand>
  10:     </dxmvvm:Interaction.Triggers>
  11: </dxg:GridControl>

Let’s review the classes for ViewModel creation.

BindableBase is a simple implementation of the INotifyPropertyChanged interface with two additional methods for implementing view model properties: SetProperty and RaisePropertyChanged. The following example demonstrates simple and complex scenarios for these methods.

   1: public class BindableObject : BindableBase {
   2:     string stringProperty1;
   3:     public string StringProperty1 {
   4:         get { return stringProperty1; }
   5:         set { SetProperty(ref stringProperty1, value, () => StringProperty1); }
   6:     }
   7:     string stringProperty2;
   8:     public string StringProperty2 {
   9:         get { return stringProperty2; }
  10:         set { SetProperty(ref stringProperty2, value, () => StringProperty2); }
  11:     }
  12:  
  13:     string stringProperty3;
  14:     public string StringProperty3 {
  15:         get { return stringProperty3; }
  16:         set {
  17:             if(SetProperty(ref stringProperty3, value, () => StringProperty3)) {
  18:                 RaisePropertiesChanged(() => StringProperty1, () => StringProperty2);
  19:             }
  20:         }
  21:     }
  22: }

Notice we’ve declared lambda expressions returning properties. This approach is very useful because it allows code checking during compilation and easy property renaming. However, in rare cases, application performance is degraded when a property is frequently updated. To accommodate these scenarios, BindableBase provides a static GetPropertyName method to calculate property names once from the ViewModel static constructor.

   1: public class BindableObject : BindableBase {
   2:     static string StringProperty1Name = string.Empty;
   3:     static BindableObject() {
   4:         BindableObject obj = null;
   5:         StringProperty1Name = BindableBase.GetPropertyName(() => obj.StringProperty1);
   6:     }
   7:  
   8:     string stringProperty1;
   9:     public string StringProperty1 {
  10:         get { return stringProperty1; }
  11:         set { SetProperty(ref stringProperty1, value, StringProperty1Name); }
  12:     }
  13: }

The BindableBase class only provides basic capabilities for implementing bindable objects, so you’ll likely use ViewModelBase as the base class for your base ViewModel. ViewModelBase descends from BindableBase and offers additional capabilities which may prove helpful. For instance, you can initialize ViewModel parameters at design-time. Set properties by overriding the OnInitializeInDesignMode method:

   1: public class ViewModel : ViewModelBase {
   2:     string stringProperty1;
   3:     public string StringProperty1 {
   4:         get { return stringProperty1; }
   5:         set { SetProperty(ref stringProperty1, value, () => StringProperty1); }
   6:     }
   7:     protected override void OnInitializeInDesignMode() {
   8:         base.OnInitializeInDesignMode();
   9:         StringProperty1 = "TestString";
  10:     }
  11: }

In complex applications, you may opt for design-time and runtime ViewModel registration via dependency injection. In simple cases, however, overriding OnInitializeInDesignMode is useful.

ViewModelBase also implements several interfaces: ISupportParentViewModel, ISupportServices, ISupportParameter. These interfaces are used by the service mechanism, to be discussed in upcoming posts.

Thank you for your time. See you!

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.