Drag and drop works quite well already with some UIElements, such as TextBox. However, you may want to using MVVM and binding to bind a command for dragging and dropping to a UIElement that doesn’t support drag and drop, such as a Grid or other container.
This can easily be done using an Attached Behavior. You may know what an Attached Property is and you may be wondering, what is the difference between an Attached Behavior and an Attached Property. An Attached Behavior is a type of an Attached Property that involves certain events, often user interaction events such a drag and drop.
Creating an Attached Behavior for use in WPF
So what is the basic recipe for creating an Attached Behavior.
- Use a public static class
- Add a static Dependency Property and and an associated PropertyChangedCallBack method (after testing, these can be private)
- Add a public method that is a static setter and getter (after testing the getter can be private)
- Add any static helper methods (usually these could be private)
- Add any additional code that is needed.
Handling Drag and Drop in WPF
If you are going to handle Drag and Drop in WPF, you need to make yourself acquainted with a particular static object called DataFormats (System.Windows.DataFormats, as there exists one in the Forms namespace too). Â This object is going to help you. Microsoft says that the DataFormats class provides a set of predefined data format names that can be used to identify data formats available in the clipboard or drag-and-drop operations.[1]Â You should take a moment to read about this class if you are not familiar with it. IDataObject and DataObject (which implements IDataObject) are also important to know and you should read about those as well.
Determine which types of data your need to handle with Drag and Drop. For each data type you plan to handle, you should check if the drag and drop data is that type and if so, handle it. You have the option to handle this in the code behind or in the behavior.
Also, with UIElements in WPF, the drag events are separate from the drop events, so we really don’t need a DragBehavior or DragAndDropBehavior, instead we only need a DropBehavior.[2] Â You should read about the drag and drop events and understand which one you need to add a behavior too. We are going to use the PreviewDrop event.
Writing a DropBehavior
Ok, now that we have gained the knowledge we need, lets write a DropBavior static class.
- Create a new class and call it DropBehavior.
- Make the class both public and static.
- Add a DependecyProperty called PreviewDropCommandProperty (Lines 13 – 26).
- Create the Setter and Getter functions. (Lines 28-57)
- Create the PropertyChangedCallBack method. (Lines 59-78)
using System.Windows.Input;
using System.Windows;
namespace MVVM
{
/// <summary>
/// This is an Attached Behavior and is intended for use with
/// XAML objects to enable binding a drag and drop event to
/// an ICommand.
/// </summary>
public static class DropBehavior
{
#region The dependecy Property
/// <summary>
/// The Dependency property. To allow for Binding, a dependency
/// property must be used.
/// </summary>
private static readonly DependencyProperty PreviewDropCommandProperty =
DependencyProperty.RegisterAttached
(
"PreviewDropCommand",
typeof(ICommand),
typeof(DropBehavior),
new PropertyMetadata(PreviewDropCommandPropertyChangedCallBack)
);
#endregion
#region The getter and setter
/// <summary>
/// The setter. This sets the value of the PreviewDropCommandProperty
/// Dependency Property. It is expected that you use this only in XAML
///
/// This appears in XAML with the "Set" stripped off.
/// XAML usage:
///
/// <Grid mvvm:DropBehavior.PreviewDropCommand="{Binding DropCommand}" />
///
/// </summary>
/// <param name="inUIElement">A UIElement object. In XAML this is automatically passed
/// in, so you don't have to enter anything in XAML.</param>
/// <param name="inCommand">An object that implements ICommand.</param>
public static void SetPreviewDropCommand(this UIElement inUIElement, ICommand inCommand)
{
inUIElement.SetValue(PreviewDropCommandProperty, inCommand);
}
/// <summary>
/// Gets the PreviewDropCommand assigned to the PreviewDropCommandProperty
/// DependencyProperty. As this is only needed by this class, it is private.
/// </summary>
/// <param name="inUIElement">A UIElement object.</param>
/// <returns>An object that implements ICommand.</returns>
private static ICommand GetPreviewDropCommand(UIElement inUIElement)
{
return (ICommand)inUIElement.GetValue(PreviewDropCommandProperty);
}
#endregion
#region The PropertyChangedCallBack method
/// <summary>
/// The OnCommandChanged method. This event handles the initial binding and future
/// binding changes to the bound ICommand
/// </summary>
/// <param name="inDependencyObject">A DependencyObject</param>
/// <param name="inEventArgs">A DependencyPropertyChangedEventArgs object.</param>
private static void PreviewDropCommandPropertyChangedCallBack(
DependencyObject inDependencyObject, DependencyPropertyChangedEventArgs inEventArgs)
{
UIElement uiElement = inDependencyObject as UIElement;
if (null == uiElement) return;
uiElement.Drop += (sender, args) =>
{
GetPreviewDropCommand(uiElement).Execute(args.Data);
args.Handled = true;
};
}
#endregion
}
}
Using the DropBehavior in XAML
Ok, now it is really easy to add this to a Grid or other UIElement that doesn’t already handle Drag and Drop. If you try to add this to a TextBlox, which already handles Drag and Drop, this even doesn’t fire.
Examples
You can add the DropBehavior to a UserControl
<UserControl x:Class="DropBehaviorExample.ExampleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:MVVM="clr-namespace:MVVM"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
AllowDrop="True"
MVVM:DropBehavior.PreviewDropCommand="{Binding PreviewDropCommand}"
>
<Grid>
</Grid>
</UserControl>
Or you can add the DropBehavior to a Grid.
<UserControl x:Class="DropBehaviorExample.ExampleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:MVVM="clr-namespace:MVVM"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
AllowDrop="True"
>
<Grid MVVM:DropBehavior.PreviewDropCommand="{Binding PreviewDropCommand}">
</Grid>
</UserControl>
And of course, you can put it on any UIElement that doesn’t already handle drag and drop.
Implementing the ICommand in the ViewModel
The following code is all you need in your ViewModel to bind the PreviewDropCommand.
#region The RelayCommand that implements ICommand
public ICommand PreviewDropCommand
{
get { return _PreviewDropCommand ?? (_PreviewDropCommand = new RelayCommand(HandlePreviewDrop)); }
set
{
_PreviewDropCommand = value;
NotifyPropertyChanged("PreviewDropCommand");
}
} private ICommand _PreviewDropCommand;
#endregion
#region The method encapsulated in the relay command
private void HandlePreviewDrop(object inObject)
{
IDataObject ido = inObject as IDataObject;
if (null == ido) return;
// Get all the possible format
string[] formats = ido.GetFormats();
// Do what you need here based on the format passed in.
// You will probably have a few options and you need to
// decide an order of preference.
}
#endregion
Hope this helps you. I know when I first rounded up this information online, it was hard to understand because of lack of preparation information, so I made sure to provide that, and also lack of comments, so I made sure to provide that.