WPF Drag and Drop from ListBox into TreeView

mrw 201 Reputation points
2021-04-19T19:32:35.587+00:00

I am trying to understand Drag and Drop login in WPF application. I have made simple example and I think I got ListBox items already, but I can't understand how to Drop selected item from ListBox into TreeView. Can somebody provide the solution to this problem so I can understand the logic? I have went through many examples, but couldn't get it working in my model. What is currently wrong?

MainWindow.xaml.cs:

using DragnDrop.Models;
using System;
using System.Collections;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace DragnDrop
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    ListBox dragSource = null;

    private void TreeView_Drop(object sender, DragEventArgs e)
    {
      TreeView parent = (TreeView)sender;
      object data = e.Data.GetData(typeof(string));
      ((IList)dragSource.ItemsSource).Remove(data);
      //parent.Items.Add(data);
      Debug.WriteLine(data);
    }

    private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      ListBox parent = (ListBox)sender;
      dragSource = parent;
      object data = GetDataFromListBox(dragSource, e.GetPosition(parent));

      if (data != null)
      {
        DragDrop.DoDragDrop(parent, data, DragDropEffects.Move);
      }
    }

    private static object GetDataFromListBox(ListBox 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;
    }
  }
}

MainWindow.xaml:

<Window x:Class="DragnDrop.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:self="clr-namespace:DragnDrop"
        xmlns:model="clr-namespace:DragnDrop.Models"
        Title="MainWindow" Height="450" Width="800">

  <Window.DataContext>
    <self:MainViewModel />
  </Window.DataContext>

  <Grid Margin="10">

    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Column="0" Grid.Row="0" Text="Category structure" FontSize="18" Margin="0,0,0,10"/>
    <TreeView Grid.Column="0" Grid.Row="1" 
              ItemsSource="{Binding TreeViewItems}" 
              FontSize="14" 
              AllowDrop="True"
              Drop="TreeView_Drop">
      <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
          <Setter Property="IsExpanded" Value="True"/>
        </Style>
      </TreeView.ItemContainerStyle>
      <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type model:CategoryItem}" ItemsSource="{Binding Path=CategoryItems}">
          <TextBlock Text="{Binding Path=ItemName}" Margin="3,3,3,3"/>
        </HierarchicalDataTemplate>
      </TreeView.Resources>
    </TreeView>

    <TextBlock Grid.Column="1" Grid.Row="0" Text="Applications" FontSize="18" Margin="0,0,0,10"/>
    <ListBox Grid.Column="1" Grid.Row="1" Name="lbTodoList" 
             HorizontalContentAlignment="Stretch" 
             FontSize="14" 
             ItemsSource="{Binding ListBoxItems}"
             PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Grid Margin="0,2">
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="*" />
              <ColumnDefinition Width="100" />
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding}" />
          </Grid>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>

  </Grid>

</Window>

Here is link to repo with working example: https://github.com/vadimffe/DragnDrop

Developer technologies | Windows Presentation Foundation
Developer technologies | XAML
Developer technologies | XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
{count} votes

Answer accepted by question author
  1. DaisyTian-1203 11,651 Reputation points Moderator
    2021-04-21T04:51:55.32+00:00

    I made a demo to drag and drop from ListBox into TreeView for you.
    xaml code:

     <StackPanel Width="800" Height="600">  
            <TreeView Width="600" Height="300" Name="tvParameters" AllowDrop="True" Background="PowderBlue" Foreground="DarkBlue" FontSize="15" >  
                <TreeView.ItemTemplate>  
                    <HierarchicalDataTemplate DataType="{x:Type local:MenuItem}" ItemsSource="{Binding Items,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">  
                        <TextBlock Text="{Binding Title ,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />  
                    </HierarchicalDataTemplate>  
                </TreeView.ItemTemplate>  
                <TreeView.ItemContainerStyle>  
                    <Style TargetType="{x:Type TreeViewItem}">  
                        <EventSetter Event="TreeViewItem.Drop" Handler="treeView_Drop"/>  
                        <Setter Property="IsExpanded" Value="True"></Setter>  
                    </Style>  
      
                </TreeView.ItemContainerStyle>  
            </TreeView>  
            <ListBox Name="lb" Width="200" Height="200" SelectionMode="Single" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown"   
                     ItemsSource="{Binding ListBoxItems,UpdateSourceTrigger=PropertyChanged}">  
            </ListBox>  
        </StackPanel>  
    

    C# code:

    public partial class Window2 : Window  
        {  
            ViewModel viewModel = new ViewModel();  
            TreeViewItem draggedItem;  
            public Window2()  
            {  
                InitializeComponent();  
                this.tvParameters.Items.Add(viewModel.TreeViewItems);  
                lb.ItemsSource = viewModel.ListBoxItems;  
            }  
      
            private void treeView_Drop(object sender, DragEventArgs e)  
            {  
                try  
                {  
                    e.Effects = DragDropEffects.None;  
                    e.Handled = true;  
                    draggedItem = new TreeViewItem() { Header = mydata };  
                    TreeViewItem TargetItem = GetNearestContainer(e.OriginalSource as UIElement);  
                    if (TargetItem != null && draggedItem != null)  
                    {  
                        e.Effects = DragDropEffects.Move;  
                        MenuItem original = viewModel.TreeViewItems;  
      
                        MenuItem target = (MenuItem)TargetItem.Header;  
      
                        if (target.Title == original.Title)  
                        {  
                            target.Level = original.Level;  
                        }  
                        else  
                        {  
                            CheckInTree(target, original.Items);  
                        }  
                        MenuItem draggedMeun = new MenuItem() { Items = null, Title = mydata.ToString() };  
                        addChild(draggedMeun, target);  
                        viewModel.ListBoxItems.Remove(mydata.ToString());  
                        lb.ItemsSource = viewModel.ListBoxItems;  
                    }  
                }  
                catch (Exception)  
                {  
                }  
            }  
      
            private MenuItem CheckInTree(MenuItem target, ObservableCollection<MenuItem> originalItems)  
            {  
                MenuItem newtarget = originalItems.Where(m => m.Title == target.Title).FirstOrDefault();  
                if (originalItems.Count != 0 && newtarget == null)  
                {  
                    foreach (MenuItem menu in originalItems)  
                    {  
                        ObservableCollection<MenuItem> menus = new ObservableCollection<MenuItem>();  
                        menus = menu.Items;  
                        CheckInTree(target, menus);  
                    }  
                }  
                return newtarget;  
            }  
      
            public void addChild(MenuItem _sourceItem, MenuItem _targetItem)  
            {  
      
                if (_targetItem == null) return;  
                MenuItem item1 = new MenuItem();  
                item1.Title = _sourceItem.Title;  
                _targetItem.Items.Add(item1);  
      
                if (_sourceItem.Items != null)  
                {  
                    foreach (MenuItem item in _sourceItem.Items)  
                    {  
                        addChild(item, item1);  
                    }  
                }  
      
            }  
      
            
      
            ListBox dragSource = null;  
            object mydata;  
            private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)  
            {  
                ListBox parent = (ListBox)sender;  
                dragSource = parent;  
                mydata = GetDataFromListBox(dragSource, e.GetPosition(parent));  
      
                if (mydata != null)  
                {  
                    DragDrop.DoDragDrop(parent, mydata, DragDropEffects.Move);  
                }  
            }  
      
            private static object GetDataFromListBox(ListBox 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 TreeViewItem GetNearestContainer(UIElement element)  
            {  
                TreeViewItem container = element as TreeViewItem;  
                while ((container == null) && (element != null))  
                {  
                    element = VisualTreeHelper.GetParent(element) as UIElement;  
                    container = element as TreeViewItem;  
                }  
                return container;  
            }  
        }  
    
    
      public class ViewModel : NotifyObject  
        {  
            public ViewModel()  
            {  
                this.ListBoxItems.Add("Fruit Ninja");  
                this.ListBoxItems.Add("Opera Browser");  
                this.ListBoxItems.Add("Notepad");  
                this.ListBoxItems.Add("Apple");  
                this.ListBoxItems.Add("Orange");  
      
      
                TreeViewItems = new MenuItem() { Title = "Menu",Level="0" };  
                MenuItem childItem1 = new MenuItem() { Title = "1", Level = "1" };  
                childItem1.Items.Add(new MenuItem() { Title = "#1.1",Level="2" });  
                childItem1.Items.Add(new MenuItem() { Title = "#1.2", Level = "2" });  
                TreeViewItems.Items.Add(childItem1);  
                TreeViewItems.Items.Add(new MenuItem() { Title = "2" ,Level = "1" });  
            }  
            public ObservableCollection<string> ListBoxItems = new ObservableCollection<string>();  
      
            public MenuItem tireeViewItems  = new MenuItem();  
            public MenuItem TreeViewItems  
            {  
                get { return tireeViewItems; }  
                set  
                {  
                    tireeViewItems = value;  
                    OnPropertyChange("TreeViewItems");  
                }  
            }  
             
        }  
      
        public class MenuItem: NotifyObject  
        {  
            private string level;  
            public string Level  
            {  
                get { return level; }  
                set  
                {  
                    level = value;  
                    OnPropertyChange("Level");  
                }  
            }  
      
      
            private string title;  
            public string Title   
            {  
                get { return title; }  
                set   
                {   
                    title = value;  
                    OnPropertyChange("Title");  
                }  
            }  
      
            private ObservableCollection<MenuItem> items = new ObservableCollection<MenuItem>();  
            public ObservableCollection<MenuItem> Items  
            {  
                get { return items; }  
                set  
                {  
                    items = value;  
                    OnPropertyChange("Items");  
                }  
            }  
        }  
      
        public class NotifyObject : INotifyPropertyChanged  
        {  
            public event PropertyChangedEventHandler PropertyChanged;  
      
            protected void OnPropertyChange(string propertyName)  
            {  
                if (PropertyChanged != null)  
                {  
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));  
                }  
            }  
      
        }  
    

    The result picture:
    89689-2.gif


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2021-04-21T11:07:26.467+00:00

    Hi,
    try my MVVM demo with your XAML and Interactivity dll from NuGet.

    XAML:

    <Window x:Class="WpfApp1.Window046"  
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
            xmlns:local="clr-namespace:WpfApp046"  
            xmlns:self="clr-namespace:WpfApp046"  
            xmlns:model="clr-namespace:WpfApp046"  
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"  
            mc:Ignorable="d"  
            Title="Window046" Height="450" Width="800">  
      <Window.DataContext>  
        <self:MainViewModel />  
      </Window.DataContext>  
      
      <Grid Margin="10">  
      
        <Grid.RowDefinitions>  
          <RowDefinition Height="Auto"/>  
          <RowDefinition Height="*"/>  
        </Grid.RowDefinitions>  
        <Grid.ColumnDefinitions>  
          <ColumnDefinition Width="*"/>  
          <ColumnDefinition Width="*"/>  
        </Grid.ColumnDefinitions>  
      
        <TextBlock Grid.Column="0" Grid.Row="0" Text="Category structure" FontSize="18" Margin="0,0,0,10"/>  
        <TreeView Grid.Column="0" Grid.Row="1"   
                   ItemsSource="{Binding TreeViewItems}"   
                   FontSize="14"   
                   AllowDrop="True">  
          <i:Interaction.Behaviors>  
            <local:DragDropBehavior/>  
          </i:Interaction.Behaviors>  
          <TreeView.ItemContainerStyle>  
            <Style TargetType="{x:Type TreeViewItem}">  
              <Setter Property="IsExpanded" Value="True"/>  
            </Style>  
          </TreeView.ItemContainerStyle>  
          <TreeView.Resources>  
            <HierarchicalDataTemplate DataType="{x:Type model:CategoryItem}" ItemsSource="{Binding Path=CategoryItems}">  
              <TextBlock Text="{Binding Path=ItemName}" Margin="3,3,3,3"/>  
            </HierarchicalDataTemplate>  
          </TreeView.Resources>  
        </TreeView>  
      
        <TextBlock Grid.Column="1" Grid.Row="0" Text="Applications" FontSize="18" Margin="0,0,0,10"/>  
        <ListBox Grid.Column="1" Grid.Row="1" Name="lbTodoList" AllowDrop="True"   
                  HorizontalContentAlignment="Stretch"   
                  FontSize="14"   
                  ItemsSource="{Binding ListBoxItems}">  
          <i:Interaction.Behaviors>  
            <local:DragDropBehavior/>  
          </i:Interaction.Behaviors>  
          <ListBox.ItemTemplate>  
            <DataTemplate>  
              <Grid Margin="0,2">  
                <Grid.ColumnDefinitions>  
                  <ColumnDefinition Width="*" />  
                  <ColumnDefinition Width="100" />  
                </Grid.ColumnDefinitions>  
                <TextBlock Text="{Binding ItemName}" />  
              </Grid>  
            </DataTemplate>  
          </ListBox.ItemTemplate>  
        </ListBox>  
      </Grid>  
    </Window>  
    

    and classes:

    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using System.Windows.Interactivity;  
    using System.Windows.Media;  
      
    namespace WpfApp046  
    {  
      public class MainViewModel  
      {  
        public MainViewModel()  
        {  
          for (int i = 1; i < 100; i++) colListBox.Add(new CategoryItem() { ItemName = $"Category {i}" });  
          cvsListBox.Source = colListBox;  
          colTreeView.Add(new CategoryItem() { ItemName = "Root" });  
          cvsTreeView.Source = colTreeView;  
        }  
        public ObservableCollection<CategoryItem> colListBox = new ObservableCollection<CategoryItem>();  
        private CollectionViewSource cvsListBox = new CollectionViewSource();  
        public ListCollectionView ListBoxItems { get => (ListCollectionView)cvsListBox.View; }  
      
        public ObservableCollection<CategoryItem> colTreeView = new ObservableCollection<CategoryItem>();  
        private CollectionViewSource cvsTreeView = new CollectionViewSource();  
        public ICollectionView TreeViewItems { get => cvsTreeView.View; }  
      }  
      
      public class CategoryItem  
      {  
        public string ItemName { get; set; }  
        public ObservableCollection<CategoryItem> CategoryItems { get; } = new ObservableCollection<CategoryItem>();  
      }  
      
      public class DragDropBehavior : Behavior<UIElement>  
      {  
        protected override void OnAttached()  
        {  
          AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown;  
          AssociatedObject.Drop += Tv_Drop;  
        }  
      
        private void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)  
        {  
          if (sender is ListBox)  
          {  
            ListBox dragSource = (ListBox)sender;  
            object data = GetDataFromListBox(dragSource, e.GetPosition(AssociatedObject));  
            if (data != null) DragDrop.DoDragDrop(dragSource, data, DragDropEffects.Move);  
          }  
        }  
      
        private static object GetDataFromListBox(ListBox 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)  
          {  
            e.Effects = DragDropEffects.None;  
            e.Handled = true;  
            // Verify that this is a valid drop and then store the drop target  
            CategoryItem targetItem = (e.OriginalSource as TextBlock)?.DataContext as CategoryItem;  
            MainViewModel vm = (sender as TreeView).DataContext as MainViewModel;  
            if (targetItem != null && vm != null)  
            {  
              object data = e.Data.GetData(typeof(CategoryItem));  
              ((ListCollectionView)vm.ListBoxItems).Remove(data);  
              targetItem.CategoryItems.Add((CategoryItem)data);  
              e.Effects = DragDropEffects.Move;  
            }  
          }  
        }  
      }  
    }  
    

    Result:

    89877-x.gif

    1 person found this answer helpful.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.