Table of Contents
Drag and Drop

Overview

EO.Wpf TreeView supports drag and drop inside the same TreeView control, between different TreeView controls, or between an EO.Wpf ListBox and an EO.Wpf.TreeView control. The control where the drag and drop originates is usually termed as "source", where the control on which user drops the item is usually termed as "target". This topic describes various scenarios where the "source" is:

You can also drag from an EO.Wpf TreeView to an EO.Wpf ListBox (with the TreeView being the "source" and the ListBox being the "target"). The steps are similar but they are discussed here.

Drag Source Is The Same TreeView Control

To allow drag drop inside the same TreeView control, simply set the TreeView's AllowDragItem and AllowDropItem to true. Optionally you can set AllowDropBetweenItems to true to allow user dropping an item in between two items. The following code demonstrates these features:

XAML
<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:eo="http://schemas.essentialobjects.com/wpf/"
        Title="MainWindow" Height="250" Width="350">
    <StackPanel>
        <eo:TreeView Width="200" Height="200" HorizontalAlignment="Left"
            AllowDragItem="True"
            AllowDropItem="True"
            AllowDropBetweenItems="True">
            <eo:TreeViewItem Header="Parent Item 1">
                <eo:TreeViewItem Header="Child Item 1"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item 2"></eo:TreeViewItem>
            </eo:TreeViewItem>
            <eo:TreeViewItem Header="Parent Item 2">
                <eo:TreeViewItem Header="Child Item 3"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item 4"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item 5"></eo:TreeViewItem>
            </eo:TreeViewItem>
        </eo:TreeView>
    </StackPanel>
</Window>

The following image demonstrates dragging "Child Item 4" in between "Child Item 1" and "Child Item 2". A line is highlighted between the two target items to indicate the drop location.

The above sample uses TreeViewItem statically declared in the XAML file. In this case when user drags and drops the item, the corresponding TreeViewItem element is moved (it can not be copied). In case the TreeView is populated from a data source, the underlying data source is modified. For example, if the underlying data source is an ObservableCollection object, then when a TreeViewItem is being moved to a different location, the corresponding element in the ObservableCollection will be moved. In order for such modification to succeed, the data source:

  • Must not be readonly. For example, if the data source is a string array, then it can not delete an item. In that case the original item will not be moved;
  • Must support change notifications. When the underlying collection is changed, the TreeView must be notified so that it can updates its UI accordingly;

Because of these two reasons, it is recommended that you use ObservableCollection as the data source if you intend to enable drag and drop.

The default behavior for drag and drop an item is to move the item. You can enable item copy by setting TreeView.AllowDragCopy or TreeViewItem.AllowDragCopy to true. When this property is set to true, user can hold down the control key while drag and drop to copy an item. Note item copy can only be performed if the items are populated from a data source. A TreeViewItem declared directly through XAML or created with code cannot be copied. Alternatively, you can use a fixed sized data source, such as a string array. When a fixed sized data source is used, the item will always be copied since it can not be deleted from the original data source.

Drag Source Is Another TreeView Control

Drag and drop between two TreeView controls are very much identical to drag and drop within the same TreeView. You must set the source's AllowDragItem and the target item's AllowDropItem to true. The following code has two TreeView controls and both TreeView has AllowDragItem, AllowDropItem and AllowDropInBetweenTwoItems set to true.

XAML
<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:eo="http://schemas.essentialobjects.com/wpf/"
        Title="MainWindow" Height="250" Width="350">
    <StackPanel>
        <eo:TreeView Width="200" Height="200" HorizontalAlignment="Left"
                AllowDragItem="True"
                AllowDropItem="True"
                AllowDropBetweenItems="True">
            <eo:TreeViewItem Header="Parent Item A1">
                <eo:TreeViewItem Header="Child Item A1"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item A2"></eo:TreeViewItem>
            </eo:TreeViewItem>
            <eo:TreeViewItem Header="Parent Item A2">
                <eo:TreeViewItem Header="Child Item A3"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item A4"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item A5"></eo:TreeViewItem>
            </eo:TreeViewItem>
        </eo:TreeView>
        <eo:TreeView Width="200" Height="200" HorizontalAlignment="Left"
                AllowDragItem="True"
                AllowDropItem="True"
                AllowDropBetweenItems="True">
            <eo:TreeViewItem Header="Parent Item B1">
                <eo:TreeViewItem Header="Child Item B1"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item B2"></eo:TreeViewItem>
            </eo:TreeViewItem>
            <eo:TreeViewItem Header="Parent Item B2">
                <eo:TreeViewItem Header="Child Item B3"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item B4"></eo:TreeViewItem>
                <eo:TreeViewItem Header="Child Item B5"></eo:TreeViewItem>
            </eo:TreeViewItem>
        </eo:TreeView>
    </StackPanel>
</Window>

The following image demonstrates item "Child Item B1" (an item from the second TreeView) is being dragged in between two items in the first TreeView: "Parent Item A2" and "Child Item A3":

Drag Source Is A ListBox Control

EO.Wpf ListBox and EO.Wpf TreeView uses the same drag and drop mechanism. So it is possible to drag an item from a ListBox into a TreeView. However since it usually does not make sense to directly "move" a ListBoxItem into a TreeView, it often requires you to handle the TreeView's ItemDrop event to carry out specific logic for the drop action. The following sample demonstrates one of such scenarios. User can drag a name from a ListBox to match a picture in the TreeView.

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:eo="http://schemas.essentialobjects.com/wpf/"
        Title="MainWindow" Height="250" Width="450">
    <StackPanel Orientation="Horizontal">
        <eo:TreeView x:Name="tvCelebrities" Width="200" Height="200" HorizontalAlignment="Left"
            AllowDropItem="True" ItemDragOver="tvCelebrities_ItemDragOver" ItemDrop="tvCelebrities_ItemDrop">
            <eo:TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=Celebrities}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <eo:Bitmap Grid.Column="0" Source="{Binding Image}" Margin="2"></eo:Bitmap>
                        <TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center" Margin="5"></TextBlock>
                    </Grid>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition></ColumnDefinition>
                                    <ColumnDefinition></ColumnDefinition>
                                </Grid.ColumnDefinitions>
                                <eo:Bitmap Grid.Column="0" Source="{Binding Image}" Margin="2"></eo:Bitmap>
                                <TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center" Margin="5"></TextBlock>
                            </Grid>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </eo:TreeView.ItemTemplate>
        </eo:TreeView>
        <eo:ListBox x:Name="lstNames" 
            Width="200" Height="200" HorizontalAlignment="Left"
            AllowDragItem="True">
        </eo:ListBox>
    </StackPanel> 
</Window>

The following screenshot demonstrates the result:

Key points of interest in the above code are:

  • The ListBox's ItemsSource is a string array. A string array is fixed sized, thus when user drags an item from the ListBox to the TreeView, the source item is NOT deleted from the ListBox;
  • The code handles the TreeView's ItemDragOver event to disable drop on top level items (celebrity categories);
  • The code handles the TreeView's ItemDrop event to update the associated Celebrity object's name property when a drop occurs. This overrides the default drop behavior, which is creating a child item using the source item data;