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 Rad Tree View-DnDTree List-Result

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.

<Grid x:Name="LayoutRoot" 
          Margin="8" 
          Background="White"> 
    <Grid.Resources> 
        <DataTemplate x:Key="WishlistProduct"> 
            <StackPanel Orientation="Horizontal"> 
                <TextBlock Foreground="#FF000000" Text="{Binding Name}" /> 
                <TextBlock Foreground="#FF000000" Text=", (" /> 
                <TextBlock Foreground="#FF000000" Text="{Binding UnitPrice}" /> 
                <TextBlock Foreground="#FF000000" Text=")" /> 
            </StackPanel> 
        </DataTemplate> 
        <DataTemplate x:Key="ProductTemplate"> 
            <telerik:RadDockPanel MaxWidth="200" Margin="2"> 
                <TextBlock Foreground="{telerik:Windows8Resource ResourceKey=MarkerBrush}" 
                           Text="{Binding Name}" 
                           telerik:RadDockPanel.Dock="Top" /> 
                <TextBlock Foreground="{telerik:Windows8Resource ResourceKey=AccentBrush}" 
                           Text="{Binding UnitPrice}" 
                           telerik:RadDockPanel.Dock="Left" /> 
                <TextBlock Margin="2 0 0 0" 
                           Foreground="{telerik:Windows8Resource ResourceKey=StrongBrush}" 
                           Text="{Binding Description}" 
                           TextWrapping="Wrap" 
                           telerik:RadDockPanel.Dock="Left" /> 
            </telerik:RadDockPanel> 
        </DataTemplate> 
        <telerik:HierarchicalDataTemplate x:Key="CategoryTemplate" 
                                  ItemTemplate="{StaticResource ProductTemplate}" 
                                  ItemsSource="{Binding Items}"> 
            <StackPanel Orientation="Horizontal"> 
                <Image Width="16" 
                       Height="16" 
                       Margin="3" 
                       VerticalAlignment="Center" 
                       Source="Images/folder_icon.png" /> 
                <TextBlock Margin="2" 
                           VerticalAlignment="Center" 
                           Foreground="{telerik:Windows8Resource ResourceKey=MarkerBrush}" 
                           Text="{Binding Name}" /> 
            </StackPanel> 
        </telerik:HierarchicalDataTemplate> 
        <Style TargetType="telerik:RadTreeViewItem"> 
            <Setter Property="IsExpanded" Value="True" /> 
        </Style> 
    </Grid.Resources> 
    <Grid.RowDefinitions> 
        <RowDefinition /> 
        <RowDefinition /> 
    </Grid.RowDefinitions> 
    <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="2" /> 
        <ColumnDefinition Width="3" /> 
    </Grid.ColumnDefinitions> 
    <!--  All Products  --> 
    <telerik:RadTreeView x:Name="allProductsView" 
                         Grid.Row="0" 
                         Grid.RowSpan="2" 
                         Grid.Column="0" 
                         Margin="0,0,5,0" 
                         IsDragDropEnabled="True" 
                         ItemTemplate="{StaticResource CategoryTemplate}" 
                         Padding="5" /> 
    <!--  Whishlist  --> 
    <ListBox x:Name="wishlistView" 
             Grid.Row="0" 
             Grid.Column="1" 
             FontSize="11" 
             ItemTemplate="{StaticResource WishlistProduct}" 
             Padding="5" 
             SelectionMode="Extended"> 
        <ListBox.Resources> 
            <DataTemplate x:Key="DraggedItemTemplate"> 
                <StackPanel> 
                    <StackPanel Orientation="Horizontal"> 
                        <TextBlock Text="Dragging:" /> 
                        <TextBlock FontWeight="Bold" Text="{Binding CurrentDraggedItem.Name}" /> 
                    </StackPanel> 
                    <StackPanel Orientation="Horizontal"> 
                        <TextBlock MinWidth="45" 
                                   FontWeight="Bold" 
                                   Text="{Binding CurrentDropPosition}" /> 
                        <TextBlock Foreground="Gray" Text=", (" /> 
                        <TextBlock Text="{Binding CurrentDraggedOverItem.Name}" /> 
                        <TextBlock Foreground="Gray" Text=")" /> 
                    </StackPanel> 
                </StackPanel> 
            </DataTemplate> 
        </ListBox.Resources> 
    </ListBox> 
</Grid> 

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:

<!--  Note: With this style we make the ListBoxItems draggable:  --> 
<Style TargetType="ListBoxItem"> 
    <Setter Property="telerik:DragDropManager.AllowCapturedDrag" Value="True" /> 
</Style> 

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 online demo.

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.

using Telerik.Windows.DragDrop; 
using Telerik.Windows.Controls; 
using Telerik.Windows.DragDrop.Behaviors; 
using Telerik.Windows.Controls.TreeView; 
 
public class ListBoxDragDropBehavior 
{ 
    private System.Windows.Controls.ListBox _associatedObject; 
    /// <summary> 
    /// object that will be associated with the ListBox instance that enables the behavior 
    /// </summary> 
    public System.Windows.Controls.ListBox AssociatedObject 
    { 
        get 
        { 
            return _associatedObject; 
        } 
        set 
        { 
            _associatedObject = value; 
        } 
    } 
 
    private static Dictionary<System.Windows.Controls.ListBox, ListBoxDragDropBehavior> instances; 
 
    static ListBoxDragDropBehavior() 
    { 
        instances = new Dictionary<System.Windows.Controls.ListBox, ListBoxDragDropBehavior>(); 
    } 
 
    public static bool GetIsEnabled(DependencyObject obj) 
    { 
        return (bool)obj.GetValue(IsEnabledProperty); 
    } 
 
    public static void SetIsEnabled(DependencyObject obj, bool value) 
    { 
        ListBoxDragDropBehavior behavior = GetAttachedBehavior(obj as System.Windows.Controls.ListBox); 
 
        behavior.AssociatedObject = obj as System.Windows.Controls.ListBox; 
 
        if (value) 
        { 
            behavior.Initialize(); 
        } 
        else 
        { 
            behavior.CleanUp(); 
        } 
        obj.SetValue(IsEnabledProperty, value); 
    } 
 
    // Using a DependencyProperty as the backing store for IsEnabled state of the behavior 
    public static readonly DependencyProperty IsEnabledProperty = 
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(ListBoxDragDropBehavior), 
            new PropertyMetadata(new PropertyChangedCallback(OnIsEnabledPropertyChanged))); 
 
    public static void OnIsEnabledPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
    { 
        SetIsEnabled(dependencyObject, (bool)e.NewValue); 
    } 
 
    private static ListBoxDragDropBehavior GetAttachedBehavior(System.Windows.Controls.ListBox listBox) 
    { 
        if (!instances.ContainsKey(listBox)) 
        { 
            instances[listBox] = new ListBoxDragDropBehavior(); 
            instances[listBox].AssociatedObject = listBox; 
        } 
 
        return instances[listBox]; 
    } 
 
    //initializes the behavior by detaching from any existing DragDropManager event handlers and attaching new DragDropManager event handlers 
    protected virtual void Initialize() 
    { 
        this.UnsubscribeFromDragDropEvents(); 
        this.SubscribeToDragDropEvents(); 
    } 
 
    //cleans up after disabling the behavior by detaching from the DragDropManager event handlers 
    protected virtual void CleanUp() 
    { 
        this.UnsubscribeFromDragDropEvents(); 
    } 
 
    //attaching new DragDropManager event handlers 
    private void SubscribeToDragDropEvents() 
    { 
 
    } 
 
    //unsubscribing from the DragDropManager event handlers 
    private void UnsubscribeFromDragDropEvents() 
    { 
 
    } 
} 
Imports Telerik.Windows.DragDrop 
Imports Telerik.Windows.Controls 
Imports Telerik.Windows.DragDrop.Behaviors 
Imports Telerik.Windows.Controls.TreeView 
 
Public Class ListBoxDragDropBehavior 
    Private _associatedObject As System.Windows.Controls.ListBox 
    ''' <summary> ''' 
    ''' object that will be associated with the ListBox instance that enables the behavior ''' 
    ''' </summary> ''' 
    Public Property AssociatedObject() As System.Windows.Controls.ListBox 
        Get 
            Return _associatedObject 
        End Get 
        Set(value As System.Windows.Controls.ListBox) 
            _associatedObject = value 
        End Set 
    End Property 
 
    Private Shared instances As Dictionary(Of System.Windows.Controls.ListBox, ListBoxDragDropBehavior) 
 
    Shared Sub New() 
        instances = New Dictionary(Of System.Windows.Controls.ListBox, ListBoxDragDropBehavior)() 
    End Sub 
 
    Public Shared Function GetIsEnabled(obj As DependencyObject) As Boolean 
        Return CBool(obj.GetValue(IsEnabledProperty)) 
    End Function 
 
    Public Shared Sub SetIsEnabled(obj As DependencyObject, value As Boolean) 
        Dim behavior As ListBoxDragDropBehavior = GetAttachedBehavior(TryCast(obj, System.Windows.Controls.ListBox)) 
 
        behavior.AssociatedObject = TryCast(obj, System.Windows.Controls.ListBox) 
 
        If value Then 
            behavior.Initialize() 
        Else 
            behavior.CleanUp() 
        End If 
        obj.SetValue(IsEnabledProperty, value) 
    End Sub 
 
    ' Using a DependencyProperty as the backing store for IsEnabled state of the behavior ' 
    Public Shared ReadOnly IsEnabledProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsEnabled", GetType(Boolean), GetType(ListBoxDragDropBehavior), New PropertyMetadata(New PropertyChangedCallback(AddressOf OnIsEnabledPropertyChanged))) 
 
    Public Shared Sub OnIsEnabledPropertyChanged(dependencyObject As DependencyObject, e As DependencyPropertyChangedEventArgs) 
        SetIsEnabled(dependencyObject, CBool(e.NewValue)) 
    End Sub 
 
    Private Shared Function GetAttachedBehavior(listBox As System.Windows.Controls.ListBox) As ListBoxDragDropBehavior 
        If Not instances.ContainsKey(listBox) Then 
            instances(listBox) = New ListBoxDragDropBehavior() 
            instances(listBox).AssociatedObject = listBox 
        End If 
 
        Return instances(listBox) 
    End Function 
 
    'initializes the behavior by detaching from any existing DragDropManager event handlers and attaching new DragDropManager event handlers' 
    Protected Overridable Sub Initialize() 
        Me.UnsubscribeFromDragDropEvents() 
        Me.SubscribeToDragDropEvents() 
    End Sub 
 
    'cleans up after disabling the behavior by detaching from the DragDropManager event handlers' 
    Protected Overridable Sub CleanUp() 
        Me.UnsubscribeFromDragDropEvents() 
    End Sub 
 
    'attaching new DragDropManager event handlers' 
    Private Sub SubscribeToDragDropEvents() 
 
    End Sub 
 
    'unsubscribing from the DragDropManager event handlers' 
    Private Sub UnsubscribeFromDragDropEvents() 
 
    End Sub 
End Class 

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.

//attaching new DragDropManager event handlers 
private void SubscribeToDragDropEvents() 
{ 
    DragDropManager.AddDragInitializeHandler(this.AssociatedObject, OnDragInitialize); 
    DragDropManager.AddGiveFeedbackHandler(this.AssociatedObject, OnGiveFeedback); 
    DragDropManager.AddDropHandler(this.AssociatedObject, OnDrop); 
    DragDropManager.AddDragDropCompletedHandler(this.AssociatedObject, OnDragDropCompleted); 
    DragDropManager.AddDragOverHandler(this.AssociatedObject, OnDragOver); 
} 
 
//unsubscribing from the DragDropManager event handlers 
private void UnsubscribeFromDragDropEvents() 
{ 
    DragDropManager.RemoveDragInitializeHandler(this.AssociatedObject, OnDragInitialize); 
    DragDropManager.RemoveGiveFeedbackHandler(this.AssociatedObject, OnGiveFeedback); 
    DragDropManager.RemoveDropHandler(this.AssociatedObject, OnDrop); 
    DragDropManager.RemoveDragDropCompletedHandler(this.AssociatedObject, OnDragDropCompleted); 
    DragDropManager.RemoveDragOverHandler(this.AssociatedObject, OnDragOver); 
 
} 
private void OnDragInitialize(object sender, DragInitializeEventArgs e) 
{ 
} 
 
private void OnGiveFeedback(object sender, Telerik.Windows.DragDrop.GiveFeedbackEventArgs e) 
{ 
} 
 
private void OnDragDropCompleted(object sender, DragDropCompletedEventArgs e) 
{ 
} 
 
private void OnDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
} 
 
private void OnDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
} 
'attaching new DragDropManager event handlers' 
Private Sub SubscribeToDragDropEvents() 
    DragDropManager.AddDragInitializeHandler(Me.AssociatedObject, AddressOf OnDragInitialize) 
    DragDropManager.AddGiveFeedbackHandler(Me.AssociatedObject, AddressOf OnGiveFeedback) 
    DragDropManager.AddDropHandler(Me.AssociatedObject, AddressOf OnDrop) 
    DragDropManager.AddDragDropCompletedHandler(Me.AssociatedObject, AddressOf OnDragDropCompleted) 
    DragDropManager.AddDragOverHandler(Me.AssociatedObject, AddressOf OnDragOver) 
End Sub 
 
'unsubscribing from the DragDropManager event handlers' 
Private Sub UnsubscribeFromDragDropEvents() 
    DragDropManager.RemoveDragInitializeHandler(Me.AssociatedObject, AddressOf OnDragInitialize) 
    DragDropManager.RemoveGiveFeedbackHandler(Me.AssociatedObject, AddressOf OnGiveFeedback) 
    DragDropManager.RemoveDropHandler(Me.AssociatedObject, AddressOf OnDrop) 
    DragDropManager.RemoveDragDropCompletedHandler(Me.AssociatedObject, AddressOf OnDragDropCompleted) 
    DragDropManager.RemoveDragOverHandler(Me.AssociatedObject, AddressOf OnDragOver) 
 
End Sub 
Private Sub OnDragInitialize(sender As Object, e As DragInitializeEventArgs) 
End Sub 
 
Private Sub OnGiveFeedback(sender As Object, e As Telerik.Windows.DragDrop.GiveFeedbackEventArgs) 
End Sub 
 
Private Sub OnDragDropCompleted(sender As Object, e As DragDropCompletedEventArgs) 
End Sub 
 
Private Sub OnDrop(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
End Sub 
 
Private Sub OnDragOver(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
End Sub 

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:

using Telerik.Windows.Controls; 
 
public class DropIndicationDetails : ViewModelBase 
{ 
    private object currentDraggedItem; 
    private DropPosition currentDropPosition; 
    private object currentDraggedOverItem; 
 
    public object CurrentDraggedOverItem 
    { 
        get 
        { 
            return currentDraggedOverItem; 
        } 
        set 
        { 
            if (this.currentDraggedOverItem != value) 
            { 
                currentDraggedOverItem = value; 
                OnPropertyChanged("CurrentDraggedOverItem"); 
            } 
        } 
    } 
 
    public int DropIndex { get; set; } 
 
    public DropPosition CurrentDropPosition 
    { 
        get 
        { 
            return this.currentDropPosition; 
        } 
        set 
        { 
            if (this.currentDropPosition != value) 
            { 
                this.currentDropPosition = value; 
                OnPropertyChanged("CurrentDropPosition"); 
            } 
        } 
    } 
 
    public object CurrentDraggedItem 
    { 
        get 
        { 
            return this.currentDraggedItem; 
        } 
        set 
        { 
            if (this.currentDraggedItem != value) 
            { 
                this.currentDraggedItem = value; 
                OnPropertyChanged("CurrentDraggedItem"); 
            } 
        } 
    } 
} 
Imports Telerik.Windows.Controls 
 
Public Class DropIndicationDetails 
    Inherits ViewModelBase 
    Private m_currentDraggedItem As Object 
    Private m_currentDropPosition As DropPosition 
    Private m_currentDraggedOverItem As Object 
 
    Public Property CurrentDraggedOverItem() As Object 
        Get 
            Return m_currentDraggedOverItem 
        End Get 
        Set(value As Object) 
            If Me.m_currentDraggedOverItem IsNot value Then 
                m_currentDraggedOverItem = value 
                OnPropertyChanged("CurrentDraggedOverItem") 
            End If 
        End Set 
    End Property 
 
    Public Property DropIndex() As Integer 
        Get 
            Return m_DropIndex 
        End Get 
        Set(value As Integer) 
            m_DropIndex = Value 
        End Set 
    End Property 
    Private m_DropIndex As Integer 
 
    Public Property CurrentDropPosition() As DropPosition 
        Get 
            Return Me.m_currentDropPosition 
        End Get 
        Set(value As DropPosition) 
            If Me.m_currentDropPosition <> value Then 
                Me.m_currentDropPosition = value 
                OnPropertyChanged("CurrentDropPosition") 
            End If 
        End Set 
    End Property 
 
    Public Property CurrentDraggedItem() As Object 
        Get 
            Return Me.m_currentDraggedItem 
        End Get 
        Set(value As Object) 
            If Me.m_currentDraggedItem IsNot value Then 
                Me.m_currentDraggedItem = value 
                OnPropertyChanged("CurrentDraggedItem") 
            End If 
        End Set 
    End Property 
End Class 

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:

private void OnDragInitialize(object sender, DragInitializeEventArgs e) 
{ 
    DropIndicationDetails details = new DropIndicationDetails(); 
    var listBoxItem = e.OriginalSource as System.Windows.Controls.ListBoxItem ?? (e.OriginalSource as FrameworkElement).ParentOfType<System.Windows.Controls.ListBoxItem>(); 
 
    var item = listBoxItem != null ? listBoxItem.DataContext : (sender as System.Windows.Controls.ListBox).SelectedItem; 
    details.CurrentDraggedItem = item; 
 
    IDragPayload dragPayload = DragDropPayloadManager.GeneratePayload(null); 
 
    dragPayload.SetData("DraggedData", item); 
    dragPayload.SetData("DropDetails", details); 
 
    e.Data = dragPayload; 
 
    e.DragVisual = new DragVisual() 
    { 
        Content = details, 
        ContentTemplate = this.AssociatedObject.Resources["DraggedItemTemplate"] as DataTemplate 
    }; 
    e.DragVisualOffset = e.RelativeStartPoint; 
    e.AllowedEffects = DragDropEffects.All; 
    e.Handled = true; 
} 
Private Sub OnDragInitialize(sender As Object, e As DragInitializeEventArgs) 
    Dim details As New DropIndicationDetails() 
    Dim listBoxItem = If(TryCast(e.OriginalSource, System.Windows.Controls.ListBoxItem), TryCast(e.OriginalSource, FrameworkElement).ParentOfType(Of System.Windows.Controls.ListBoxItem)()) 
 
    Dim item = If(listBoxItem IsNot Nothing, listBoxItem.DataContext, TryCast(sender, System.Windows.Controls.ListBox).SelectedItem) 
    details.CurrentDraggedItem = item 
 
    Dim dragPayload As IDragPayload = DragDropPayloadManager.GeneratePayload(Nothing) 
 
    dragPayload.SetData("DraggedData", item) 
    dragPayload.SetData("DropDetails", details) 
 
    e.Data = dragPayload 
 
    e.DragVisual = New DragVisual() With { 
        .Content = details, 
        .ContentTemplate = TryCast(Me.AssociatedObject.Resources("DraggedItemTemplate"), DataTemplate) 
    } 
    e.DragVisualOffset = e.RelativeStartPoint 
    e.AllowedEffects = DragDropEffects.All 
End Sub 

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.

private void OnGiveFeedback(object sender, Telerik.Windows.DragDrop.GiveFeedbackEventArgs e) 
{ 
    e.SetCursor(Cursors.Arrow); 
    e.Handled = true; 
} 
Private Sub OnGiveFeedback(sender As Object, e As Telerik.Windows.DragDrop.GiveFeedbackEventArgs) 
    e.SetCursor(Cursors.Arrow) 
    e.Handled = True 
End Sub 

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:

private void OnDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
    TreeViewDragDropOptions options = DragDropPayloadManager.GetDataFromObject(e.Data, TreeViewDragDropOptions.Key) as TreeViewDragDropOptions; 
    if (options == null) 
    { 
        e.Effects = DragDropEffects.None; 
        e.Handled = true; 
        return; 
    } 
    var draggedItem = options.DraggedItems.FirstOrDefault(); 
    var itemsType = (this.AssociatedObject.ItemsSource as IList).AsQueryable().ElementType; 
 
 
    if (draggedItem.GetType() != itemsType) 
    { 
        e.Effects = DragDropEffects.None; 
    } 
    else 
    { 
        (options.DragVisual as TreeViewDragVisual).IsDropPossible = true; 
        options.DropAction = DropAction.Move; 
        options.UpdateDragVisual(); 
    } 
    e.Handled = true; 
} 
Private Sub OnDragOver(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
    Dim options As TreeViewDragDropOptions = TryCast(DragDropPayloadManager.GetDataFromObject(e.Data, TreeViewDragDropOptions.Key), TreeViewDragDropOptions) 
    If options Is Nothing Then 
        e.Effects = DragDropEffects.None 
        e.Handled = True 
        Return 
    End If 
    Dim draggedItem = options.DraggedItems.FirstOrDefault() 
    Dim itemsType = TryCast(Me.AssociatedObject.ItemsSource, IList).AsQueryable().ElementType    
 
    If draggedItem.GetType <> itemsType Then 
        e.Effects = DragDropEffects.None 
    Else 
        TryCast(options.DragVisual, TreeViewDragVisual).IsDropPossible = True 
        options.DropAction = DropAction.Move 
        options.UpdateDragVisual() 
    End If 
    e.Handled = True 
End Sub 

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.

private void OnDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
    TreeViewDragDropOptions options = DragDropPayloadManager.GetDataFromObject(e.Data, TreeViewDragDropOptions.Key) as TreeViewDragDropOptions; 
    if (options == null) return; 
    var draggedItem = options.DraggedItems.FirstOrDefault(); 
 
    if (draggedItem == null) 
    { 
        return; 
    } 
 
    if (e.Effects != DragDropEffects.None) 
    { 
        var collection = (sender as System.Windows.Controls.ListBox).ItemsSource as IList; 
        collection.Add(draggedItem); 
    } 
 
    e.Handled = true; 
} 
 
private void OnDragDropCompleted(object sender, DragDropCompletedEventArgs e) 
{ 
    var draggedItem = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData"); 
 
    if (e.Effects != DragDropEffects.None) 
    { 
        var collection = (sender as System.Windows.Controls.ListBox).ItemsSource as IList; 
        collection.Remove(draggedItem); 
    } 
} 
Private Sub OnDrop(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
    Dim options As TreeViewDragDropOptions = TryCast(DragDropPayloadManager.GetDataFromObject(e.Data, TreeViewDragDropOptions.Key), TreeViewDragDropOptions) 
    If options Is Nothing Then 
        Return 
    End If 
    Dim draggedItem = options.DraggedItems.FirstOrDefault() 
 
    If draggedItem Is Nothing Then 
        Return 
    End If 
 
    If e.Effects <> DragDropEffects.None Then 
        Dim collection = TryCast(TryCast(sender, System.Windows.Controls.ListBox).ItemsSource, IList) 
        collection.Add(draggedItem) 
    End If 
 
    e.Handled = True 
End Sub 
 
Private Sub OnDragDropCompleted(sender As Object, e As DragDropCompletedEventArgs) 
    Dim draggedItem = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData") 
 
    If e.Effects <> DragDropEffects.None Then 
        Dim collection = TryCast(TryCast(sender, System.Windows.Controls.ListBox).ItemsSource, IList) 
        collection.Remove(draggedItem) 
    End If 
End Sub 

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.
Rad Tree View-DnDTree List

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.

DragDropManager.AddDropHandler(allProductsView, OnDrop); 
DragDropManager.AddDropHandler(allProductsView, OnDrop) 

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:

IList destinationItems = null; 
private void OnDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
    var data = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData"); 
    if (data == null) return; 
    if (e.Effects != DragDropEffects.None) 
    { 
        var destinationItem = (e.OriginalSource as FrameworkElement).ParentOfType<RadTreeViewItem>(); 
        var dropDetails = DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails") as DropIndicationDetails; 
 
        if (destinationItems != null) 
        { 
            int dropIndex = dropDetails.DropIndex >= destinationItems.Count ? destinationItems.Count : dropDetails.DropIndex < 0 ? 0 : dropDetails.DropIndex; 
            this.destinationItems.Insert(dropIndex, data); 
        } 
    } 
} 
Dim destinationItems As IList = Nothing 
Private Sub OnDrop(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
    Dim data = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData") 
    If data Is Nothing Then 
        Return 
    End If 
    If e.Effects <> DragDropEffects.None Then 
        Dim destinationItem = TryCast(e.OriginalSource, FrameworkElement).ParentOfType(Of RadTreeViewItem)() 
        Dim dropDetails = TryCast(DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails"), DropIndicationDetails) 
 
        If destinationItems IsNot Nothing Then 
            Dim dropIndex As Integer = If(dropDetails.DropIndex >= destinationItems.Count, destinationItems.Count, If(dropDetails.DropIndex < 0, 0, dropDetails.DropIndex)) 
            Me.destinationItems.Insert(dropIndex, data) 
        End If 
    End If 
End Sub 

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.

private void OnItemDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e) 
{ 
    var item = (e.OriginalSource as FrameworkElement).ParentOfType<RadTreeViewItem>(); 
    if (item == null) 
    { 
        e.Effects = DragDropEffects.None; 
        e.Handled = true; 
        return; 
    } 
    var position = GetPosition(item, e.GetPosition(item)); 
    if (item.Level == 0 && position != DropPosition.Inside) 
    { 
        e.Effects = DragDropEffects.None; 
        e.Handled = true; 
        return; 
    } 
    RadTreeView tree = sender as RadTreeView; 
    var draggedData = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData"); 
    var dropDetails = DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails") as DropIndicationDetails; 
 
    if ((draggedData == null && dropDetails == null)) 
    { 
        return; 
    } 
    if (position != DropPosition.Inside) 
    { 
        e.Effects = DragDropEffects.All; 
 
        destinationItems = item.Level > 0 ? (IList)item.ParentItem.ItemsSource : (IList)tree.ItemsSource; 
        int index = destinationItems.IndexOf(item.Item); 
        dropDetails.DropIndex = position == DropPosition.Before ? index : index + 1; 
    } 
    else 
    { 
        destinationItems = (IList)item.ItemsSource; 
        int index = 0; 
 
        if (destinationItems == null) 
        { 
            e.Effects = DragDropEffects.None; 
        } 
        else 
        { 
            e.Effects = DragDropEffects.All; 
            dropDetails.DropIndex = index; 
        } 
    } 
 
    dropDetails.CurrentDraggedOverItem = item.Item; 
    dropDetails.CurrentDropPosition = position; 
 
    e.Handled = true; 
} 
 
private DropPosition GetPosition(RadTreeViewItem item, Point point) 
{ 
    double treeViewItemHeight = 24; 
    if (point.Y < treeViewItemHeight / 4) 
    { 
        return DropPosition.Before; 
    } 
    else if (point.Y > treeViewItemHeight * 3 / 4) 
    { 
        return DropPosition.After; 
    } 
 
    return DropPosition.Inside; 
} 
Private Sub OnItemDragOver(sender As Object, e As Telerik.Windows.DragDrop.DragEventArgs) 
    Dim item = TryCast(e.OriginalSource, FrameworkElement).ParentOfType(Of RadTreeViewItem)() 
    If item Is Nothing Then 
        e.Effects = DragDropEffects.None 
        e.Handled = True 
        Return 
    End If 
    Dim position = GetPosition(item, e.GetPosition(item)) 
    If item.Level = 0 AndAlso position <> DropPosition.Inside Then 
        e.Effects = DragDropEffects.None 
        e.Handled = True 
        Return 
    End If 
    Dim tree As RadTreeView = TryCast(sender, RadTreeView) 
    Dim draggedData = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData") 
    Dim dropDetails = TryCast(DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails"), DropIndicationDetails) 
 
    If (draggedData Is Nothing AndAlso dropDetails Is Nothing) Then 
        Return 
    End If 
    If position <> DropPosition.Inside Then 
        e.Effects = DragDropEffects.All 
 
        destinationItems = If(item.Level > 0, DirectCast(item.ParentItem.ItemsSource, IList), DirectCast(tree.ItemsSource, IList)) 
        Dim index As Integer = destinationItems.IndexOf(item.Item) 
        dropDetails.DropIndex = If(position = DropPosition.Before, index, index + 1) 
    Else 
        destinationItems = DirectCast(item.ItemsSource, IList) 
        Dim index As Integer = 0 
 
        If destinationItems Is Nothing Then 
            e.Effects = DragDropEffects.None 
        Else 
            e.Effects = DragDropEffects.All 
            dropDetails.DropIndex = index 
        End If 
    End If 
 
    dropDetails.CurrentDraggedOverItem = item.Item 
    dropDetails.CurrentDropPosition = position 
 
    e.Handled = True 
End Sub 
 
Private Function GetPosition(item As RadTreeViewItem, point As Point) As DropPosition 
    Dim treeViewItemHeight As Double = 24 
    If point.Y < treeViewItemHeight / 4 Then 
        Return DropPosition.Before 
    ElseIf point.Y > treeViewItemHeight * 3 / 4 Then 
        Return DropPosition.After 
    End If 
 
    Return DropPosition.Inside 
End Function 

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.
Rad Tree View-DnDList To Tree

You can download a runnable project of the demonstrated example from our online SDK repository here, after navigating to TreeView/DragDropTreeViewToControls.

See Also

In this article
Not finding the help you need? Improve this article