I have implemented the Drag&Drop from a ListBox to a TreeView, as displayed below.
Drag&Drop from ListBox to TreeView
Now I want, for example, after dropping items to TreeView, I can 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:
<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:DragDropBehavior />
</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: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 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:
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);
}
}
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 = 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)
{
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));
}
}
}
}
}
Model:
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;
}
}
}
Questions:
- How to enable my desired feature that move items inside TreeView?
- What is the difference of
DragDropEffects
? I also try to sete.Effects
toDragDropEffects.Move
, but the result looks the same to me. - What is the difference between
MouseLeftButtonDown
andPreviewMouseLeftButtonDown
. I see lots of similar events that 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 just want to work inside the DragDropBehavior class to implement the feature that drag and droo items insdie the TreeView.