Friday, September 25, 2009

My best WPF magic for Model-View-ViewModel (M-V-VM) with Routed Commands

Some time ago I had the required free time to think about a nice way to implement a set of classes and ideas that will allow me to build a WPF application that:

- Honors the Model-View-ViewModel pattern.
- Keeps the view as simple as possible, ideally as a template and no code-behind.
- Uses the power of the Command pattern in WPF (Routed Commands), keeping the View agnostic about the logic encapsulated in commands.

After some attempts, the result are 2 classes that make this all possible in a very elegant way, I think. The first class is the ViewModelBase (fragment):

public abstract class ViewModelBase
  // Something like the "Null object pattern"
  protected static ViewModelBase EmptyViewModel = new EmptyViewModel();

  public IEnumerable CommandBindings { get; }
  public IEnumerable InputBindings { get; }
  protected abstract void PrepareBindings();

The ViewModelBase class, as its name implies, is supposed to be the base class of all the ViewModels, and it is here where you define what to do when a RoutedCommand is executed. It is the glue between the RoutedCommands in the View and the command execution logic in the ViewModel.

Then, there is the ViewModelControl class (full text):

public class ViewModelControl : ContentControl
  protected override void OnContentChanged(object oldContent, object newContent)
    ViewModelBase viewModel = newContent as ViewModelBase;
    if (viewModel != null)
      foreach (var binding in viewModel.CommandBindings)
      foreach (var binding in viewModel.InputBindings)
    base.OnContentChanged(oldContent, newContent);

This control takes care of associating the CommandBindings to the visual elements so that the RoutedCommands mechanism works.

And that's it. There are some implementation details missing, but with these classes in place you just have to:

1- Create your ViewModel classes inheriting from ViewModelBase.
2- Provide the implementation of their PrepareBindings methods.
3- Create the templates for your ViewModels.
4- Bind your controls to the commands.

Using this implementation you'll get these results:

1- You'll have your RoutedCommands.
2- Logic for these commands will be in your ViewModel classes, where you can test it.
3- No event handlers, no code-behinds required.

Mix this with INotifyPropertyChanged in your ViewModels and DataModels and you'll have a cool architecture for your View. Here's an example:

1. Define a routed commands:

public class ViewCommands
  public static RoutedCommand MyRoutedCommand;
  static ViewCommands()
    MyRoutedCommand = new RoutedCommand("MyRouted", typeof(ViewCommands));

2. Define a ViewModel:

public class MyViewModel : ViewModelBase
  protected override void PrepareBindings()
    this.AddCommandBindings(new CommandBinding(
    ViewCommands. MyRoutedCommand,
      (x, y) => DoSomethingIfMyRoutedIsExecutedOnThisViewModel(),
      (x, y) => y.CanExecute = CanMyRoutedCommandBeExecuted));

3. Finally define a view with a ViewModelControl and some templates for your ViewModels and DataModels:

    <DataTemplate DataType="{x:Type models:MyViewModel}">
      <Button Content="Execute MyRoutedCommand" Command="model:ViewCommands.MyRoutedCommand" />
    <baseViews:ViewModelControl Content="{Binding}" />

That's it. It works very nicely, try it.

I think I'll be posting a more complete article about this soon. In the mean time:

- What do you think?
- Want to see some real code?

Monday, September 21, 2009

TEAM's Db4o Management Studio v0.1.0 is out

Many times in the latest couple of months I've found my self hoping for a tool that would allow me to inspect a db4o database without using the classes that were used to save the data.

One of these situations is when I'm migrating an old classes' schema into a new one, and I really need to see what's inside the database to implement the migration.

After so much hoping and dissapointment on the Db4o Object Manager, I decided to implement a simple tool to do the job. The result is a very simple (and crude) application.

Don't expect anything fancy, but please, send any comments and suggestions, and feel free to copy the idea and make a better tool. The db4o community will thank you.

You can get the application from sourceforge here