Who would not like to implement a sleek drag and drop in minutes? I definitely would.
This is why I find the new RadDragAndDrop a welcome addition to the controls.
Straight to the point then: How to implement a simple drag/drop? To answer this question I will walk you through the DragDrop example that is available here:
http://demos.telerik.com/silverlight/#Examples/DragAndDrop/FirstLook
This example shows that the DragDrop can be used with any controls/objects, not just the Telerik RadControls.
Few simple things to remember: Every object that will be dragged must have AllowDrag property to true, it is an attached property and in xaml it can be set the following way:
<Rectangle dragDrop:RadDragAndDropManager.AllowDrag="True" />
And in code:
RadDragAndDropManager.SetAllowDrag(anyControl, true);
Similarly, we need to set the AllowDrop property to true for all objects that will be the end destination (drop targets) for the drag/drop. This would enable the DragDrop manager to recognize and consider these objects but this is not enough to implement a real drag-drop.
Since any object can be dragged to any other (visual) object, it is impossible for the manager to take care of all the possible actions on its own. Sure when you are dragging objects within a TreeView the items may be related. But what if you drag a button over a ListBox? Furhtermore, any business object may be behind similar visual representation, so there is no way for the manager to know what to do in a particular context.
The DragDrop manager “speaks” to the application through events. There are two types of events, one where the manager asks “What to do now?” and another which just informs us “Ok, this is what I have done”. These events are routed events which originate both at the target and at the destination of a drag/drop operation.
Routed events are our best friend in a complex application since they can be handled not only at the originating object but at any of its visual/logical parents. The DragDrop events are:
The “Drop” events originate at the target, the “Drag” at the source, the “Info” events want to inform us about something and the “Query” events ask for permission or allow us to tweak the settings for the DragDrop. The answer to the query event is its QueryResult property which can be set to true/false/null.
Here is how you sign up for events:
//using Telerik.Windows;
this.AddHandler(RadDragAndDropManager.DragQueryEvent, new EventHandler<DragDropQueryEventArgs>(OnDragQuery));
//The handler method:
private void OnDraqQuery(object sender, DragDropQueryEventArgs e)
{
//Handler code goes here.
}
Each of these events expose a DragDropOptions object which holds information about the current DragDrop event, this object allows us to specify what exactly will be dragged, whether an arrow will be seen and a few other things.
The most important bit of the options available is the Status of the DragDrop. This is an enumeration which shows the progress of the DragDrop event and specifies the question the application is being asked or the information it is given. So let’s have a look at them:
Phew, this is just one of the properties of the Drag/Drop options. What about the others?
The details for the other available options will be discussed in a subsequent post, let’s just have a look at the one we need for this example:
Generally in drag/drop operations the source and destination should try to pretend that they have no idea who the other party is, i.e. a drag/drop operation should be treated separately and based on what is being dragged, not where the operation has originated from. For example, if we have two ListBoxes all the complexity of multiple routed events seems a bit unnecessary but in a real rich application you may want to implement drag/drop between different controls over different windows. In such case a CoverFlow item dragged over a GridView will make sense, since they may be represented by one and the same (type of) business object, and that really means any object, with any control as its destination or source.
The example in question is part of the examples for the RadControls and can be downloaded with any release after the Silverlight RC0 update.
The DraggableListBox class there is just an example of how you can extend any control and add drag/drop functionality to it. Of course, extending a control is not needed and in many cases you would not have to do it. It is necessary to do it this particular example, since the ListBox offers no way to get the ListBoxItems if the control is databound (strange, isn’t it? :)). The telerik controls of course have the ItemsContainerGenerator exactly for this purpose. We need the ListBoxItems to tell them that they will be draggable, this happens in the PrepareContainerForItemOverride where we have access to any item – generated or not:
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
RadDragAndDropManager.SetAllowDrag(element, true);
}
Also we have wrapped the routed events in a xaml-friendly CLR events, altough it makes no difference how we sign up for them. This way we get a bit of the mighty VS code-completion as well :)
public event EventHandler<DragDropQueryEventArgs> DragQuery
{
add
{
this.AddHandler(RadDragAndDropManager.DragQueryEvent, value);
}
remove
{
this.RemoveHandler(RadDragAndDropManager.DragQueryEvent, value);
}
}
We sign up for those events in the constructor and mark the ListBox as a drop target:
public DraggableListBox()
{
RadDragAndDropManager.SetAllowDrop(this, true);
this.DragQuery += new EventHandler<DragDropQueryEventArgs>(OnDragQuery);
this.DragInfo += new EventHandler<DragDropEventArgs>(OnDragInfo);
this.DropQuery += new EventHandler<DragDropQueryEventArgs>(OnDropQuery);
this.DropInfo += new EventHandler<DragDropEventArgs>(OnDropInfo);
}
In each of the DragDrop events we implement the drag/drop logic. This means that any item can be dragged and it can be dropped in the ListBox as long it is not already there. Also when a drag/drop is complete the item will be removed from the source Listbox and will be added to the destination Listbox. For example this is the DropQuery method:
void OnDropQuery(object sender, DragDropQueryEventArgs e)
{
var box = sender as DraggableListBox;
var itemsSource = box.ItemsSource as IList<ApplicationInfo>;
var payload = e.Options.Payload as ApplicationInfo;
e.QueryResult = payload != null && !itemsSource.Contains(payload);
}
We make use o fthe fact that we know the type of the dragged object and the the fact that the ListBoxes are bound to an ItemsSource. With a bit of extra code the handlers can be made more generic.
In the example the ListBoxes and the DragCue have different templates for the same business objects (ApplicationInfo) which give different look for the same underlying data.
I hope that this will be enough to get you started with the DragDrop.
Do tell me what you think!
What more would you like to know about?
What example would you like to see in the next post?