Implement Drag and Drop Between TreeView and ListBox
The goal of this tutorial is to show you how to implement drag and drop between RadTreeView and ListBox.
The final result should look like this
Implement Drag and Drop Between TreeView and ListBox
For the purpose of this example, you will need to create an empty application project and open it in Visual Studio.
The first step is to add references to the following assemblies:
- Telerik.Windows.Controls.Navigation
- Telerik.Windows.Controls
- Telerik.Windows.Data
Then you can define the controls in your view. As the purpose of this tutorial is to demonstrate how to implement drag and drop operations, we won't focus on the definitions of the controls in xaml. However, please note to set the RadTreeView IsDragDropEnabled property to true.
The next step is to use the DragDropManager to enable the drop operation on the ListBox control.
Find the ListBox declaration and set its AllowDrop property to True.
Now that the ListBox allows drop operations, we need to make sure that the ListItems are draggable. We can do so by applying an implicit style that sets the DragDropManager.AllowCapturedDrag attached property to True on every ListItem:
telerik is alias for the following namespace declaration:
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Next we'll need to populate the controls with data but as the ViewModels definitions are outside the scope of this tutorial, we'll omit them for clarity. We'll only assume that the RadTreeView is populated with categories and each category displays a list of products. While, the ListBox displays a collection of products. This means that we'll have to implement a logic that allows you to drag a product from the ListBox and drop it inside a category in the tree.
Please have in mind that if the ItemsSource of the RadTreeView (RadTreeViewItem) is not an IList, then the drop operation won't be allowed. Moreover, we recommend using a collection that implements the INotifyCollectionChanged interface, for instance an ObservableCollection, as only then the changes implemented in the RadTreeView underlying data collection will be reflected in the UI of the control.
Now we're getting to the actual DragDrop implementation. And we'll start with configuring the ListBox as a participant in drag and drop operations.
Please note that this article is based on the TreeToGrid demo, which you can find by navigating to the DragAndDrop examples ->Tree To Grid Drag.
In this tutorial we'll use a custom behavior to define the ListBox DragDrop behavior. Essentially the behavior will attach handlers for the following events:
- DragInitialize
- GiveFeedback
- Drop
- DragDropCompleted
- DragOver
You can find more information about the DragDropManager events in this tutorial.
So we basically need a class that provides:
a ListBox object that will be associated with the ListBox instance that enables the behavior
IsEnabled property to control the enabled state of the behavior
a Dictionary that holds all ListBox instances enabling the behavior
methods that attach and detach the DragDropManager event handlers.
Now let's attach handlers for the DragDropManager events listed above. We'll do that in the SubscribeToDragDropEvents() method and we'll detach from these handlers in the implementation of the UnsubscribeFromDragDropEvents() method.
Once we do so, we can start implementing our drag/drop logic. And we'll always have to keep in mind that we need to drag items from the RadTreeView and drop them in the ListBox and vice versa. This means that when we initialize a drag, we need to access the data displayed in the dragged ListBoxItem and add the data to the DragInitializeEventArgs Data object. This will allow us to pass the dragged information during the drag/drop operation. Also, in order to provide the user with elaborate visual information during the drag operation, we can create a helper class that describes the currently dragged item, the item that the drag is passing over, the drop position and the drop index calculated based on the drop position. For instance, we can use the following class definition:
We can use this definition to also pass and keep the drag operation details in the Data object provided by the DragDropManager. This means that we can implement the following drag initialization:
Then we can implement the GiveFeedback event handler, which is quite straight-forward. As this event enables the drop source to give feedback information to the user, we'll use it to change the appearance of the mouse pointer to indicate the effects allowed by the drop target.
Next, we have to handle the DragOver event and implement a logic that decides if the current drag operation is supported over the current drop destination. In this example, we won't allow a drag operation within the ListBox, which means that we have to make sure that the drag operation is allowed only if it originates from the RadTreeView control.
RadTreeView drag operation creates an object of type TreeViewDragDropOptions that holds all information related to the drag. You can read more about the properties exposed by the type in the Drag and Drop article.
As the data object passed by a drag operation started in RadTreeView should be of type TreeViewDragDropOptions, this means that you can try to extract this object and if the operation is unsuccessful, then the drag doesn't originate from a RadTreeView. Furthermore, we'll have to make sure that the dragged data type matches the data type displayed in the ListBox - in our example this means that we'll make sure we're dragging products. So finally, we can create the following OnDragOver() implementation:
Finally we'll have to implement the actual drop logic and we'll also have to update the collection displayed in the source of the drag operation accordingly. We'll do that in the handlers of the Drop and DragDropCompleted events.
With this the ListBox drag/drop logic is complete. All we have finalize now, is the definition of the control to enable the ListBoxDragDropBehavior. So please find the declaration of the ListBox and add the following line: 'example:ListBoxDragDropBehavior.IsEnabled="True"', where example is an alias pointing to the namespace in which we've defined the ListBoxDragDropBehavior class.
If you run the solution now, you should be able to drag items from the RadTreeView and drop them in the ListBox.
And if you start dragging an item from the ListBox, you'll be able to get the item's data and even display an informative tooltip describing the dragged item. However, this tooltip won't be updated while dragging over the RadTreeView and you won't be able to drop the item among the tree items. This is due to the fact that the RadTreeView built-in drag/drop logic is implemented to handle only drag originating from a RadTreeView. Therefore, you will also have to set up the tree to process a drag coming from a ListBox.
In order to configure the RadTreeView to process the drop of an item coming from another control, you need to attach a handler for the DragDropManager Drop event.
In the handler you need to get the dragged data, find the position where the item should be dropped at - before, after or inside a particular RadTreeViewItem and implement the drop by adding the dragged data in the RadTreeView ItemsSource collection:
And to make the dragging operation more informative, we can also subscribe to the DragDropManager DragOver event to update the drag operation information tooltip while dragging over the RadTreeView.
If you take a closer look at the above code snippet, you'll notice that it basically tracks the current drop destination and position. Then based on its type and value, the code decides if the drag operation is allowed, updating the visual representation of the operation at the same time.
With that last piece of code, our application is ready. It can now provide a fluent drag-drop operation between a RadTreeView and a ListBox control.
Find a runnable project of the previous example in the WPF Samples GitHub repository.