WPF “Drag and Drop” – doing it the MVVM way

I have been recently involved in a WPF/Prism project where one of the requirements was to allow the user to move rather large sums of money with the mouse. Think of moving the money from one account to another but with potential of serious loss if you drop it in between. When I trawled the net for examples of drag & drop in WPF, I noticed that the vast majority of them deal with the visual aspects of the operation, mixing business logic with the code behind which in turn results in code which is pretty much un-testable (in a unit testing sense). If  you take a closer look at any of the drag and drop code, it becomes obvious that the operation has a number of distinct aspects to it, some of which can be delegated to the “business logic component” offering potentially better testability:

  1. Drag and drop is initiated when the user presses the mouse and moves it by a certain distance. This requires event handling somewhere in the code behind. This code has to be potentially repeated over and over again for different controls.
  2. At the time the drag and drop is initiated, we hand over the mouse handling to the OS, but we still need to provide the data and we need to indicate what can be done with the object (Move, Copy etc). This would be best handled by the view model.
  3. While the mouse is being dragged we may need to provide some visual feedback as to what is going to happen when the object will get dropped. This again would be best handled by the view model but needs to be initiated from an event handler.
  4. Once the object is dropped, we just need to consume it but the operation requires Drop event handler in the code behind.

After a bit of head scratching and various discussions with John, I managed to come up wit ha solution which allows the drag and drop logic to be both reusable and testable. The main actors are as follows:

  1. DragSourceBehaviour  implements the event handling required to initiate the drag operation
  2. An object implementing IDragSource interface provides the data to be dragged
  3. DropTargetBehaviour implements drop related event handlers
  4. Object implementing IDropTarget handles the business logic of the “drop”
  5. Helper classes provide shortcuts for implementing both IDropTarget and IDragSource

The rest of this post discusses details of the implementation.

Handling the “Drag”

The start the drag & drop we need two event handlers: one to handle PreviewMouseButtonDown event and record the position and another one to handle PreviewMouseMove to see if the mouse have moved far enough to initiate drag & drop. Implementing those handlers over and over again in the code behind is not my idea of fun, so obviously another solution is required and a WPF behaviour fits the bill nicely. The drag and drop operation also needs a piece of data that will be dragged and we need to know what sort of drag operation will be supported: as indicated earlier this would be best handled by the view model.

To glue the event handling aspects and the data handling aspect together, I came up with the attached property of IDragSource type exposed by the DragSourceBehaviour. Once the property is set to a non null value, the behaviour will take care of the event handling while still delegating the task of providing data to implementation of IDragSource. I hope the following fragment from the DragSourceBehaviour class explains it all:

   1:  private static void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
   2:  {
   3:      _startPoint = e.GetPosition(null);
   4:  }
   5:   
   6:  private static void MouseLeave(object sender, MouseEventArgs e)
   7:  {
   8:      // Need to reset since the mouse left in order to prevent mouse movement 
   9:      // in another element to pick drag an drop
  10:      _startPoint = null;
  11:  }
  12:   
  13:  private static void PreviewMouseMove(object sender, MouseEventArgs e)
  14:  {
  15:      if (e.LeftButton != MouseButtonState.Pressed || _startPoint == null)
  16:          return;
  17:   
  18:      if(!HasMouseMovedFarEnough(e))
  19:          return;
  20:   
  21:      var dependencyObject = (FrameworkElement) sender;
  22:      var dataContext = dependencyObject.GetValue(FrameworkElement.DataContextProperty);
  23:      var dragSource = GetDragSource(dependencyObject);
  24:   
  25:      if (dragSource.GetDragEffects(dataContext) == DragDropEffects.None)
  26:          return;
  27:   
  28:      DragDrop.DoDragDrop(dependencyObject,
  29:                          dragSource.GetData(dataContext),
  30:                          dragSource.GetDragEffects(dataContext));
  31:              
  32:  }
  33:   

 

The object implementing IDragSource interface does not have to be in any sense “related” to the visual initiating the operation, but whoever implements the IDragSource would be often interested in what is the data context of the object and for this reason IDragSource takes object “dataContext” parameter to both of it’s methods

   1:      /// <summary>
   2:      /// Business end of the drag source
   3:      /// </summary>
   4:      public interface IDragSource
   5:      {
   6:          /// <summary>
   7:          /// Gets the supported drop effects.
   8:          /// </summary>
   9:          /// <param name="dataContext">The data context.</param>
  10:          /// <returns></returns>
  11:          DragDropEffects GetDragEffects(object dataContext);
  12:   
  13:          /// <summary>
  14:          /// Gets the data.
  15:          /// </summary>
  16:          /// <param name="dataContext">The data context.</param>
  17:          /// <returns></returns>
  18:          object GetData(object dataContext);
  19:      }

Implementing IDragSource interface over and over again may become tedious very quickly so I came up wit ha shortcut for implementing the interface which simply takes two delegates to be executes as and when required:

   1:  /// <summary>
   2:  /// Gets the (drag) source of cookies.
   3:  /// </summary>
   4:  /// <value>The source of cookies.</value>
   5:  public IDragSource SourceOfCookies
   6:  {
   7:      get
   8:      {
   9:          if (_source == null)
  10:              _source = new DragSource<CookieJar>(GetDragEffects, GetData);
  11:   
  12:          return _source;
  13:      }
 

Handling the “Drop”

The drop operation is being handled in a similar fashion. This time however the attached property is of type IDropTarget and here’s how it is defined.

   1:  public interface IDropTarget
   2:  {
   3:      /// <summary>
   4:      /// Gets the effects.
   5:      /// </summary>
   6:      /// <param name="dataObject">The data object.</param>
   7:      /// <returns></returns>
   8:      DragDropEffects GetDropEffects(IDataObject dataObject);
   9:   
  10:      /// <summary>
  11:      /// Drops the specified data object
  12:      /// </summary>
  13:      /// <param name="dataObject">The data object.</param>
  14:      void Drop(IDataObject dataObject);
  15:  }

Similarly the code in the DropTargetBehaviour class delegates the task of handling the data to the object implementing IDropTarget:

   1:  private static void Drop(object sender, DragEventArgs e)
   2:  {
   3:      var dropTarget = GetDropTarget((DependencyObject)sender);
   4:   
   5:      dropTarget.Drop(e.Data);
   6:      e.Handled = true;
   7:  }
   8:   
   9:  private static void DragOver(object sender, DragEventArgs e)
  10:  {
  11:      var dropTarget = GetDropTarget((DependencyObject)sender);
  12:   
  13:      e.Effects = dropTarget.GetDropEffects(e.Data);
  14:      e.Handled = true;            
  15:  }

Putting it all together

To test the entire machinery I have developed a sample application which allows you to drag cookies between cookie jars. But there are constraints to it: you cannot drag anything out of an empty jar, and each jar will accept no mo re than ten cookies. These are the business rules which are enforced by corresponding unit tests.

image

The following fragment of XAML illustrates how the control is glued together with the DragSource and DropTarget behaviours:

<DragDrop:CookieJarControl Behaviours:DragSourceBehaviour.DragSource="{Binding SourceOfCookies}" 
                            Behaviours:DropTargetBehaviour.DropTarget="{Binding CookieSink}"
                            AllowDrop="True"/>

The source code for the entire project is available as part of the SharpFellows.Toolkit. As we develop more and more reusable goodies I am sure they will make it’s way into the library. Feel free to use it in any which way you want (this includes copy-pasting of suitable fragments) but please let us know if  you find it useful!

August 20 2010

WPF/Silverlight MVVM Design Data - create once and share with Unit Tests

Getting data appearing in design view in Blend is key to being able to manipulate the your layout and styles without needing to run up your app (to quote Marcin, ‘nudge, nudge, nudge instead of nudge-F5-waaaait, nudge-F5-waaaaait’)

As soon as a colleague pointed out to me VS2010 now supports Blend’s d:DataContext I brought forward getting things ‘working in in design view’, as I realised here was a way to quickly and easily wire up a test rig of data in C# that I could share between my unit tests, a mock session of my WPF/Silverlight app and design view in both Blend and VS2010 (rather than xaml test data for UI, C# or for tests/runtime test UI). Down to it …

Working backwards, here’s my end result (Bob and Alice are a List<Person> bound to a ListView). Doesn’t look like much, but it works with all xaml controls including DataGrids/TreeViews etc, so you should quickly start to see the power in simplicity.

image

Key XAML:

<UserControl ... xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:md="clr-namespace:MvvmDemo" mc:Ignorable="d"
        d:DesignHeight="600" d:DesignWidth="800"
        d:DataContext="{x:Static md:DesignData.PersonListViewModel}"
        x:Name="view" DataContext="{Binding ElementName=view, Path=ViewModel}">
... <TextBox Text="{Binding Filter.PersonName}"
... <DatePicker SelectedDate="{Binding Filter.BirthDate}"
... <ListBox ItemsSource="{Binding FilteredPeople}">

By the way, this is very cool: the VS2010 Properties window now has navigation of my ViewModel object hierarchy, as long as I’m bound to my real DataContext in xaml. Still prefer the speed of resharper smart auto-completion though (ctrl+alt+space).

image

Anyway, back to the xaml … note the d:DataContext is bound to a static DesignData class & property. Here you could either inject a fake/mock repository and have the ViewModel do its thing, or stop it from loading and populate it in the DesignData class. Both are good techniques in unit testing and design data, and can be used interchangeably. Here I demo both (the viewmodel loads existing people from the repository, hence in design the fake, and I populate the form filter fields).

public static class DesignData
{
    static DesignData()
    {
        Container.Register<IPersonRepository, FakePersonRepository>();
    }

    public static PersonListViewModel PersonListViewModel
    {
        get
        {
            return new PersonListViewModel
                       {
                           Filter = new PersonFilter
                                        {
                                            PersonName = "Ca",
                                            BirthDate = DateTime.Now.AddYears(-20)
                                        }
                       };
        }
    }
}

I inject & reuse the FakeRepository in my TestUI exe (a separate exe so as not to pollute my release code with accidental fake data). 

And just for good measure, here’s a kind of contrived test – the fake data doesn’t really effect the test but you get the idea: write once, use everywhere - same as the rest of your code.

[TestMethod]
public void search_on_name_should_filter()
{
   // ARRANGE 
   _sut = new PersonListViewModel();
   var people = new FakePersonRepository().GetPeople();
   _sut.People.AddRange(people);
   people = new List<Person> { new Person {Name = "Carol"},
                        new Person {Name = "Carlos"},
                        new Person {Name = "Charlie"} };
   _sut.People.AddRange(people);
    string search = "Ca";
    _sut.PersonName = search;

   // ACT 
   _sut.Search();

   // ASSERT
   Assert.IsFalse(_sut.FilteredPeople.Any(p => !p.Name.StartsWith(search)));
   Assert.IsTrue(_sut.FilteredPeople.Any(p => p.Name.StartsWith(search)));
}

Full code here:

image

Technorati Tags: ,,
April 1 2010

Using Delegate Commands with WPF Views (UPDATED)

UPDATED: Scroll to end of post.

Please note:  the examples in this post depend on an implementation of the Presentation Model described in a previous post. 

DelegateCommand<T> is a class that comes with the Composite Application Library (CAL or PRISM), which is the latest incarnation of the Composite application block (CAB).  I have included it in my solution for an application that is based on CAB and have also created a second class DelegateCommand which extends DelegateCommand<T> simply by overriding the constructors and specifying T as object.  This second class allows you to use delegate commands when you don't need to use a command parameter.  I will not go into detail here regarding the implementation of the DelegateCommand<T> class (you can look at the code for yourself – see the attached zip file at the bottom of the post), but will stick to describing how it is used.

Delegate commands can be used instead of WPF Routed commands.  The advantage of a delegate command is that they can be bound to directly on the PresentationModel without the need for a pass through method in the View's code behind.  The command parameters are also type safe, whereas with Routed Commands, parameters are always of type object, and so need to be cast before use.

 Delegate commands are exposed on the Presentation Model as a property.  For example:

OrdersPresentationModel: AddCommand Property

public DelegateCommand<Order> AddCommand { get; set; }

Here we are declaring a DelegateCommand<T>. If the command is to be passed a command parameter, the type of the parameter should be included as the template for the command.  If not, use DelegateCommand instead (no template).

I have chosen to use automatic properties for my commands.  I therefore need to initialise the commands from the OnViewReady method.  (Alternatively, I could have included the initialisation in the getter via a null check). I do this by calling the InitialiseCommands method:

OrdersPresentationModel: InitialiseCommands Method

private void InitialiseCommands()

{

    ...

    AddCommand = new DelegateCommand<Order>(OnAdd, CanAdd);

    ...

}

 My OnAdd() and CanAdd() methods are also declared in the presentation model as follows:

OrdersPresentationModel: OnAdd and CanAdd Methods

private static bool CanAdd(Order order)

{

    return order != null;

}

private void OnAdd(Order order)

{

    if (order == null) return;

    Customer.Orders.AddUnique(Order);

}

So how do we use the command?  As simple as any other property binding:  

OrdersView

<Button Margin="5,0,5,0"

        Height="20"

        Width="70"

        Command="{Binding AddCommand}"

        CommandParameter="{Binding ElementName=ordersComboBox, Path=SelectedItem}">

 

 That's it.  Very simple.  Notice that the OnAdd and CanAdd methods are private.  How can we test them then?  Better than just testing the methods, we can actually call the command from the test.  

OrdersPresentationModelTest

public void AddCommand_AddOrder_OrderAddedToCustomersOrdersCollection()

{

    ....

   _model.AddCommand.Execute(new Order(1));

    ....

    model.AddCommand.CanExecute(new Order(1));

    ....

} 

UPDATE:  Thanks to a tip off from a colleague of mine at EMC, Marcin Kaluza, regarding Josh Smith’s excellent post Allowing CommandManager to query your ICommand objects, I have updated the DelegateCommand class by removing the RaiseCanExecuteChanged and the OnCanExecuteChanged methods and instead implementing add and remove accessors on the CanExecuteChanged event that hook the event up to the CommandManager.RequerySuggested event.  As a result, we no longer need to implement a DispatcherTimer (as suggested below) for each view to periodically poll the CanExecute methods.  I’m sure you’ll agree, this is a much neater solution and has the added bonus of removing the concern of CPU spiking, since we are no longer polling the can execute methods every half second or so, but only when the CommandManager detects conditions that might change the ability of a command to execute.  Thanks Josh!

 There is just one fly in the ointment with this approach.  The CanExecute fires only once when the view is loaded.  This seems like a flaw in the DelegateCommand, since the WPF's Routed Command regularly polls CanExecute methods.  There may now be other implementations of the delegate command that claim to have solved this problem (the Relay Command may be one of them), but I have yet to research them.  In the mean time I have implemented a DispatcherTimer that raises the RaiseCanExecuteChanged() on the delegate command every half second.  This is initialised from the OnViewReady method by calling the InitialiseCanExecuteDispatcherTimer()  method on the PresentationModel.  Ideally this should be pushed down into the DelegateCommand class, but I have yet to look into solving that particular problem.

OrdersPresentationModel: InitialiseCanExecuteDispatcherTimer Method

private void InitialiseCanExecuteDispatcherTimer()

{

    _dispatcherTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0,500) };
    _dispatcherTimer.Tick += (sender, e) => AddCommand.RaiseCanExecuteChanged();
    _dispatcherTimer.Start();

} 

Using the Presentation Model Pattern with WPF Views and CAB/CAL (PRISM)

The Established MVP Pattern
When creating WPF views for an application based on Microsoft Patterns & Practices Composite UI Application Block (CAB), a UI framework for developing applications in Windows Forms that also supports hosting WPF Views, the pattern we adopted for binding controls was to call methods from the OnViewReady method to retrieve or create the bound objects in the presenter and pipe them into the view's code behind via method calls on the views interface.  These methods in the view then set the DataContext of the target control, or indeed of the view itself, to the passed in object.  In some cases, when binding directly to an ItemsControl, the passed in object was a collection, and this was bound to the ItemsSource of the target control.  Here is an example:

Presenter:

public override void OnViewReady()

      {

          base.OnViewReady();

          BindView();

      }

      public void BindView()

      {

          ...

          View.BindCustomers(_customers);

          ...

      }

 

View Code Behind:

public void BindCustomers(ObservableCollection<CustomerBindingObject> customers)

{

    customerslistBox.ItemsSource = customers;

}

The Presentation Model
The Presentation Model is an architectural pattern introduced by Martin Fowler and is the basis of Microsoft’s more specialized MVVM (Model-View-ViewModel) pattern.  It is the favoured approach of the Composite Application Library (CAL or PRISM), the UI framework for developing applications in WPF and Silverlight,  and is employed for separating the responsibilities for the visual display of the user interface and the presentation state and behaviour.  This approach is not so different from the traditional MVP pattern, except in the way it handles data binding.

As before, methods are called from the OnViewReady method to retrieve or create the bound objects in the presenter.  But rather than piping them into the view's code behind for subsequent binding to controls in the view, the bound objects are stored in properties on the presenter.  The presenter itself, or presentation model as it should be called, is then bound directly to the data context of the view.

Now, in the Composite Application Library (CAL or PRISM), the way they implement this, seems a backward step to me.  After storing the binding objects in properties on the Presentation Model, they call a method on the view (as we did in the established MVP pattern above), and pass "this" (the instance of the presentation model) in.  They then set the DataContext of the view to the passed in presentation model, in the called method.


I felt that this unnecessarily cluttered the views code behind and so came up with what I feel is a cleaner approach.  I have created an interface called IBindableView.  This interface implements just one property shown below:

IBindableView Interface:

public interface IBindableView

{

    object DataContext { get; set; }

}

 By inheriting from this interface, the interface for a WPF view will expose the DataContext of that view.  For example:

ICustomersView Interface:

public interface ICustomersView : IBindableView

{

}

 This then allows you to directly set the DataContext from the OnViewReady method in the presenter, without needing to call methods on the view:

CustomersPresentationModel:

public override void OnViewReady()

{

    base.OnViewReady();

    ...

    View.DataContext = this;

    ...

}

You can then bind your controls directly to the properties on your presentation model in XAML.  You can also expose your commands as properties on the presentation model and bind to these directly in XAML.  I shall explain how to do this in another post on Delegate Commands.

November 24 2009
Newer Posts Older Posts