Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have implemented the Drag&Drop from a ListBox to a TreeView. Now I want, for example, after dropping items to TreeView, to drag ListBoxItem2 and drop it in the TreeViewItem1 and vice versa for other two items. I am using Behavior class from System.Windows.Interactivity library to realize such feature.

Here is the code:

XAML:
XML
<Window x:Class="TreeviewExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="clr-namespace:System.Windows.Interactivity;
              assembly=System.Windows.Interactivity"
        xmlns:local="clr-namespace:TreeviewExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <GroupBox Header="Features" Grid.Column="0" 
        Grid.Row="0" Grid.RowSpan="2" Margin="10,10,10,10">
            <!-- Features ListBox -->
            <ListBox x:Name="featureListBox"
         AllowDrop="True"
         ItemsSource="{Binding ListBoxSource}" Margin="5,10,5,10">
                <i:Interaction.Behaviors>
                    <local:ListBoxDragDropBehavior />
                </i:Interaction.Behaviors>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="0,2">
                            <TextBlock Text="{Binding ItemName}" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </GroupBox>

        <GroupBox Header="Template" Grid.Column="1" Grid.Row="0" 
                     Grid.RowSpan="2" Margin="10,10,10,10">
            <Grid>
                <TreeView Name="templateTreeview"
       ItemsSource="{Binding TreeViewSource}"
       FontSize="14"
       AllowDrop="True" Margin="5,5,5,10">
                    <i:Interaction.Behaviors>
                        <local:TreeViewDragDropBehavior />
                    </i:Interaction.Behaviors>
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="{x:Type TreeViewItem}">
                            <Setter Property="IsExpanded" 
                            Value="True" />
                        </Style>
                    </TreeView.ItemContainerStyle>
                    <TreeView.Resources>
                        <HierarchicalDataTemplate 
                        DataType="{x:Type local:ViewItem}" 
                        ItemsSource="{Binding Path=ViewItems}">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=ItemName}" 
                                Margin="0,0,10,0" />
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.Resources>
                </TreeView>
            </Grid>
        </GroupBox>
    </Grid>
</Window>


ViewModel:
C#
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace TreeviewExample
{
    public class MainViewModel
    {
        public ObservableCollection<ViewItem> 
               ListBoxSource { get; set; }
        public ObservableCollection<ViewItem> 
               TreeViewSource { get; set; }

        public MainViewModel()
        {
            ListBoxSource = new ObservableCollection<ViewItem>();
            TreeViewSource = new ObservableCollection<ViewItem>();

            ListBoxSource.Add(new ViewItem("ListBoxItem1"));
            ListBoxSource.Add(new ViewItem("ListBoxItem2"));

            ViewItem defalutView = new ViewItem("Root", 1);
            defalutView.ViewItems.Add(new ViewItem("TreeViewItem1"));
            defalutView.ViewItems.Add(new ViewItem("TreeViewItem2"));
            TreeViewSource.Add(defalutView);
        }
    }
}


Model:
C#
using System.Collections.ObjectModel;

namespace TreeviewExample
{
    public class ViewItem
    {
        public string ItemName { get; set; }
        public ObservableCollection<ViewItem> 
        ViewItems { get; } = new ObservableCollection<ViewItem>();
        public int IsVisible { get; set; }

        public ViewItem()
        { }

        public ViewItem(string name, int IsVisible = 0)
        {
            this.ItemName = name;
            this.IsVisible = IsVisible;
        }
    }
}


ListBoxDrapDropBehavior:
C#
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace TreeviewExample
{
    public class ListBoxDragDropBehavior : Behavior<UIElement>
    {
        protected override void OnAttached()
        {
            AssociatedObject.PreviewMouseLeftButtonDown +=
                             PreviewMouseLeftButtonDown;
        }

        private void PreviewMouseLeftButtonDown
                (object sender, MouseButtonEventArgs e)
        {
            if (sender is ListBox)
            {
                ListBox dragSource = (ListBox)sender;
                object data = GetDataFromSourceControl
                (dragSource, e.GetPosition(AssociatedObject));
                if (data != null) DragDrop.DoDragDrop
                (dragSource, data, DragDropEffects.Move);
            }
        }

        private static object GetDataFromSourceControl
                       (ItemsControl source, Point point)
        {
            UIElement element = source.InputHitTest(point) as UIElement;
            if (element != null)
            {
                object data = DependencyProperty.UnsetValue;
                while (data == DependencyProperty.UnsetValue)
                {
                    data = source.ItemContainerGenerator.ItemFromContainer
                           (element);
                    if (data == DependencyProperty.UnsetValue) element = 
                    VisualTreeHelper.GetParent(element) as UIElement;
                    if (element == source) return null;
                }
                if (data != DependencyProperty.UnsetValue) return data;
            }
            return null;
        }
    }
}


TreeViewDragDropBehavior:
C#
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace TreeviewExample
{
    public class TreeViewDragDropBehavior : Behavior<UIElement>
    {
        protected override void OnAttached()
        {
            AssociatedObject.PreviewMouseLeftButtonDown += 
                             PreviewMouseLeftButtonDown;
            AssociatedObject.Drop += Tv_Drop;
        }

        private void PreviewMouseLeftButtonDown
                (object sender, MouseButtonEventArgs e)
        {
            if (sender is TreeView)
            {
                TreeView dragSource = (TreeView)sender;
                object data = GetDataFromSourceControl
                (dragSource, e.GetPosition(AssociatedObject));
                if (data != null) DragDrop.DoDragDrop
                (dragSource, data, DragDropEffects.Move);
            }
        }

        private static object GetDataFromSourceControl
                       (ItemsControl source, Point point)
        {
            UIElement element = source.InputHitTest(point) as UIElement;
            if (element != null)
            {
                object data = DependencyProperty.UnsetValue;
                while (data == DependencyProperty.UnsetValue)
                {
                    data = source.ItemContainerGenerator.ItemFromContainer
                           (element);
                    if (data == DependencyProperty.UnsetValue) element = 
                    VisualTreeHelper.GetParent(element) as UIElement;
                    if (element == source) return null;
                }
                if (data != DependencyProperty.UnsetValue) return data;
            }
            return null;
        }

        private void Tv_Drop(object sender, DragEventArgs e)
        {
            if (sender is TreeView)
            {
                TreeView treeView = (TreeView)sender;
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                // Verify that this is a valid drop and 
                // then store the drop target
                ViewItem targetItem = 
                (e.OriginalSource as TextBlock)?.DataContext as ViewItem;
                MainViewModel vm = (sender as TreeView).DataContext 
                                    as MainViewModel;
                if (targetItem != null && vm != null)
                {
                    object data = e.Data.GetData(typeof(ViewItem));
                    ViewItem dragData = (ViewItem)data;
                    targetItem.ViewItems.Add
                              (new ViewItem(dragData.ItemName));
                }
            }
        }
    }
}


Questions:

  • How can I implement feature that reorders items inside TreeView?
  • What is the difference of DragDropEffects? I also try to set e.Effects to DragDropEffects.Move, but the result looks the same to me.
  • What is the difference between MouseLeftButtonDown and PreviewMouseLeftButtonDown. I see lots of similar events with or without Preview.

Could someone help me with the issue?

What I have tried:

In the PreviewMouseLeftButtonDown function, I add another case for TreeView. But I fail to get the drag data. The return value of e.Data.GetData(typeof(ViewItem)) in the Tv_Drop function is the root in the TreeView.

What I expect:

I want to drag and drop TreeViewItem inside TreeView to reorder the items.
Posted
Updated 13-Sep-23 1:00am
v5
Comments
[no name] 8-Sep-23 23:49pm    
You need to explain what's supposed to happen to "reorder the items". Adding a single item doesn't require a "reorder"; it just requires an "insert" in the proper order.
BabyHai 9-Sep-23 5:20am    
It's not to insert an item. let's say I have TreeViewItem A1 and B1, each item has its own child nodes, A1.1, A1.2 or B1.1,B1.2. Now I want to drag B1.2 before B1.1, or drag B1.2 between A1.1 and A1.2. Meanwhile, B1.2 is deleted from B1 node in second case.
This is the reorder I mean, hope I explain it right.
[no name] 9-Sep-23 11:53am    
I built a "tree maintainer", but it used selection and a couple of buttons ... to move a branch up or down (or delete, or whatever) in the hierarchy. (Copying a branch, deleting it, then inserting it somewhere else would be one function; as well as duplicating branches ... which gets into keyboard shifts to expand drag and drop) So, I use drag and drop in some cases, but not in this case.
Richard Deeming 11-Sep-23 4:38am    
Rather than trying to implement it yourself, you might want to look at GongSolutions.WPF.DragDrop[^] - it includes a sample app with an example of dragging TreeView nodes around.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900