WPF PRISM (CAL) / MVVM – DelegateCommand Extensions – Part 2

July 3, 2010

tags: ,
2 comments

Update 29/07: The project was updated, there was a minor bug in the object changed notifier.

Be sure to read the first post – Part One

After showing the most basic in part 1, let’s see a real-life example of my finalized solution.
I created a sample application which looks as follows –

image

Let me go through this very quickly –

  1. The main view is bound to a MainViewModel.
  2. The employees list view at the left is bound an EmployeeListViewModel which is a member of the MainViewModel
  3. The employee details at the right is bound to the SelectedEmployee property of the EmployeeListViewModel
  4. The top Edit / Save buttons are bound to trigger commands on the MainViewModel
  5. The application is aware of user permission state that can be changed in runtime – if so, a CAL event is published.

Let’s see the Employee model’s class –

Code Snippet
  1. public class Employee : NotifyPropertyChangedBase
  2. {
  3.     public string Name { get; set; }
  4.  
  5.     public EmployeeType Type { get; set; }
  6. }

For the sake of the example, let’s assume the following requirements should be met –

  1. The user can edit employees only if he has a write permission
  2. The user can edit an employee only if he’s an in-house employee (according to his type) regardless of the user permission

You can see that the commands which are controlled by the MainViewModel in this specific example is inherently bound to the state of the current selected employee as well as a permission context which is published through CAL events.
Additionally, we would obviously need to make our commands be state-aware of the current action. (Save should be enabled if edit is on, Edit should be disabled if edit mode is on, etc..)

At this point, you would normally go and put all that logic into your ViewModel, simply rubbish it with all of these and have the related code all scattered around.

Let’s look at my final solution and walk through it:

Code Snippet
  1. public class EmployeeViewModel : ViewModelBase
  2. {
  3.     private Employee _employee;
  4.  
  5.     public bool IsEditMode { get; set; }
  6.  
  7.     public string Name { get; set; }
  8.  
  9.     public EmployeeType Type { get; set; }
  10.  
  11.     public bool IsEditMode { get; set; }
  12.  
  13.     public bool CanUpdate
  14.     {
  15.         get { return _employee.Type == EmployeeType.InHouse; }
  16.     }
  17.  
  18.     static EmployeeViewModel()
  19.     {
  20.         //A place to define dependent properties
  21.         //When 'Type' is notified as changed, so will 'CanUpdate'
  22.         //This solution supports refactoring!
  23.         AddPropertyDependency((EmployeeViewModel o) => o.CanUpdate)
  24.             .DependsOn(o => o.Type);
  25.     }
  26. }
  27.  
  28. public class MainViewModel : ViewModelBase
  29. {
  30.     IEventAggregator _eventAggregator;
  31.     
  32.     #region Commands
  33.     static SourcePathDescription SelectedItemPathDescription = SourcePathDescription.Create((MainViewModel o) => o.EmployeesViewModel.SelectedEmployee);
  34.     static SourceMembersDescription CanUpdateDescription = SourceMembersDescription.Create<EmployeeViewModel>(o => o.CanUpdate, o => o.IsEditMode);
  35.  
  36.     private void UpdateCommands()
  37.     {
  38.         RegisterCommand(DelegateCommandEx.Create("EditCommand", () => EmployeesViewModel.SelectedEmployee.IsEditMode = true)
  39.             .WithObjectNotifiers(
  40.                 ObjectChangedNotifier<EmployeeViewModel>.FromPath(this, SelectedItemPathDescription, CanUpdateDescription)
  41.                 .CanExecute(false, o => o.CanUpdate && !o.IsEditMode)
  42.                 .Create())
  43.             .WithNotifiers(new CalEventNotifier<PermissionChangedEvent, PermissionType>(
  44.                 _eventAggregator, p => p == PermissionType.Write)));
  45.  
  46.         RegisterCommand(DelegateCommandEx.Create("SaveCommand", () => EmployeesViewModel.SelectedEmployee.IsEditMode = false)
  47.             .WithObjectNotifiers(
  48.                 ObjectChangedNotifier<EmployeeViewModel>.FromPath(this, SelectedItemPathDescription, CanUpdateDescription)
  49.                 .CanExecute(false, o => o.CanUpdate && o.IsEditMode)
  50.                 .Create())
  51.             .WithNotifiers(new CalEventNotifier<PermissionChangedEvent, PermissionType>(
  52.                 _eventAggregator, p => p == PermissionType.Write)));
  53.     }
  54.     #endregion
  55. }

Some code was minimized for brevity (automatic properties instead of full ones for example)

As you can see, I created factory methods to make the creation of commands in a fluent interface to make the code more readable.
Let’s summarize things –

  1. General
    1. I created factory methods to make the creation of commands in a fluent interface to make the code more readable
    2. I could implement the INotifyCanExecuteChanged I created for the specific need of these commands, but I was aiming for a more generic reusable components
    3. We can see two generic components I implemented
      1. ObjectChangedNotifier
        1. The notifier is bound to a certain object path or the actual source instance (another overload)
        2. It supports full property paths – you can see that the path to the source used here goes through two properties – o.EmployeesViewModel.SelectedEmployee.
          It binds to the whole property path, if the EmployeesViewModel will be changed on the MainViewModel – the change will be notified!
        3. The container description holds the path to the source the command is bound to
        4. The source description holds the properties the command is bound to. Only when these properties are notified to be changed on the source the command will execute its CanExecuteChanged
        5. The description classes that represent the bound state are created statically and then used within the instance
          This ensures best performance since we create the lambda expressions only once and reuse the description
        6. The CanExecute attached the implementation of whether the state is valid or not. The first parameter sets the value for when the bound path is null or N/A, therefore, in the CanExecute implementation I don’t need to check whether it is null
        7. The internal implementation uses weak references and the PropertyChangedEventManager to ensure there won’t be any memory leaks
      2. CalEventNotifier
        1. The notifier is bound to a certain CAL event that may be published
    4. Extra – you can see in the EmployeeViewModel the property dependency declaration – another sweet addition.
  2. Edit Command
    1. The edit command should be executable when the following is met –
      1. The employee has ‘CanUpdate’ of ‘true’
      2. The employee isn’t in edit mode already
        1. The above two are covered using the ObjectNofier
      3. The user permission is ‘Write’
        1. This is covered using the CalEventNotifier since changes in the permissions are done through the CAL’s IEventAggregator.
  3. Save Command
    1. Pretty much the same as the edit command. The only change is that it should be enabled when it IS in edit mode.

 In Conclusion – What have I accomplished here?

The commands include the logic they need in order to determine fully if it can be executed or not. No other code in any other place is needed!
If more specific implementation is needed, you can always implement the INotifyCanExecute with your own implementation and attach it to your commands.

The code is more encapsulated, changes to the command state are done in one place, no need to tamper with the state of your ViewModel.
Finally, it makes the code more centralized, more readable and with a better maintainable approach.

Update 29/07: You can download the updated source here – Zuker.WpfSamples.zip

Last Note

Currently, there is no cancel operation and when the user edits the employee’s name it is already committed before clicking on ‘Save’.
Actually, saving just removes the edit mode.
I will address this in another post in the near future, so stay tuned.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. onemennyJuly 5, 2010 ב 10:56

    i like the “AddPropertyDependency((EmployeeViewModel o) =>o.CanUpdate).DependsOn(o => o.Type);”

    i currently dont have a need for such complex solution.
    my case was different – i wanted to “know” when all delegate commands registered to a single composite commands are not active, so i manipulate the IActiveAware interface inside each view (using wpfToolkit commandReference) to change the state on each command. other than that i had to register to ActiveChanged event, and tweak the composite command to notify me when one of the command state changes

    i know its something else than what you wanted to achieve, but its a different view of things

    good work zuker!

    Reply
  2. Amir ZukerJuly 5, 2010 ב 19:09

    Hi Menny,

    IActiveAware is somewhat different in its purpose I think. It is usually referred when you wish the command would be subjective to the active context of the application in terms of active/selected UI.

    Still, IActiveAware doesn’t give you such notifier bindings out of the box, you would still have to implement a similar solution like mine that utilizes the IActiveAware implementation to get the behavior I was set to achieve.

    In my case, I wanted the command to turn into a non-executable state which helps me with Unit Testing as well.

    Well, you did say its a different view 🙂
    Thanks for the input, I appreciate it.

    Reply