Telerik blogs

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.

DragDropFirstLook

DragDrop 101

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 Events

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:

 

DragDropEvents

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.

DragDrop Phases

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:

  • None: No drag/drop is taking place; this value exists because the DragDropOptions object is available as a static property of the DragDropManager. If you access the Options object when nothing is being dragged, this will be its status.
  • DragQuery: A source object (where AllowDrag is true) is about to be dragged and we need to acknowledge that this is allowed. We need to set QueryResult = true. (the default value is null).
  • DragInProgress: Dragging is underway, no drop locations currently available.
  • DragComplete: This is the status when an event is fired to notify the source that the DragDrop event has successfully completed. Here the dragged object may be removed from the source for example.
  • DragCancel: The source is notified that the DragDrop has been cancelled, for example the user may press Esc while dragging to cancel the event or the object may be released over a target that does not accept it.
  • DropDestinationQuery: The destination is being asked whether a drop operation is possible. Since drag-drop is a two-way handshake process, the source needs to agree as well. Generally, no visual cues should be given on query events.
  • DropSourceQuery: The source object is being asked whether it is ok to drop the object at the particular destination.
  • DropPossible: The destination and the source are each notified that a drop operation is possible, here custom visuals may indicate to the user that a drop is possible.
  • DropImpossible: The drop operation has either been rejected or the user has dragged out of a valid drop target
  • DropComplete: The destination is notified that a drop has successfully completed.
  • DropCancel: The drop has been cancelled.

Phew, this is just one of the properties of the Drag/Drop options. What about the others?

DragDrop Options

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:

  • Payload: This is the actual object that gets moved from the source to the destination. Allowing/rejecting a DragDrop operation should primarily be based on it. It can be any object and it has no visual representation.
  • DragCue: The DragCue is the object that will be visually dragged, i.e. it will follow the mouse and will always be in on top of other objects.
  • ArrowCue: This is an eye-candy addition to the DragDrop, it is an object that will be stretched and rotated in a way that its left side points to the original mouse position of the drag, while its right side is the current mouse position. It looks best when this object is arrow-shaped :). You can quickly get hold of such a visual by calling RadDragAndDropManager. GenerateArrowCue()

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

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.

Next >

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?

 

About the Author

Valeri Hristov

Team Lead,
Platform Team

Comments

Comments are disabled in preview mode.