diff --git a/src/GongSolutions.WPF.DragDrop/DropInfo.cs b/src/GongSolutions.WPF.DragDrop/DropInfo.cs
index 740afed0..d8f3a9fc 100644
--- a/src/GongSolutions.WPF.DragDrop/DropInfo.cs
+++ b/src/GongSolutions.WPF.DragDrop/DropInfo.cs
@@ -13,15 +13,17 @@ namespace GongSolutions.Wpf.DragDrop
///
/// Holds information about a the target of a drag drop operation.
///
- ///
+ ///
///
- /// The class holds all of the framework's information about the current
- /// target of a drag. It is used by method to determine whether
+ /// The class holds all of the framework's information about the current
+ /// target of a drag. It is used by method to determine whether
/// the current drop target is valid, and by to perform the drop.
///
public class DropInfo : IDropInfo
{
- private readonly ItemsControl itemParent;
+ private readonly DragEventArgs eventArgs;
+ private ItemsControl itemParent;
+ private bool? acceptChildItem;
///
public object Data { get; set; }
@@ -152,6 +154,20 @@ public bool IsSameDragDropContextAsSource
///
public EventType EventType { get; }
+ ///
+ public bool AcceptChildItem
+ {
+ get => this.acceptChildItem.GetValueOrDefault();
+ set
+ {
+ if (value != this.acceptChildItem.GetValueOrDefault())
+ {
+ this.acceptChildItem = value;
+ this.Update();
+ }
+ }
+ }
+
///
/// Initializes a new instance of the DropInfo class.
///
@@ -161,6 +177,7 @@ public bool IsSameDragDropContextAsSource
/// The type of the underlying event (tunneled or bubbled).
public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo, EventType eventType)
{
+ this.eventArgs = e;
this.DragInfo = dragInfo;
this.KeyStates = e.KeyStates;
this.EventType = eventType;
@@ -200,12 +217,14 @@ public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo,
// visual target can be null, so give us a point...
this.DropPosition = this.VisualTarget != null ? e.GetPosition(this.VisualTarget) : new Point();
- if (this.VisualTarget is TabControl)
+ this.Update();
+ }
+
+ private void Update()
+ {
+ if (this.VisualTarget is TabControl && !HitTestUtilities.HitTest4Type(this.VisualTarget, this.DropPosition))
{
- if (!HitTestUtilities.HitTest4Type(this.VisualTarget, this.DropPosition))
- {
- return;
- }
+ return;
}
if (this.VisualTarget is ItemsControl itemsControl)
@@ -256,10 +275,11 @@ public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo,
var tvItemIsExpanded = tvItem is { HasHeader: true, HasItems: true, IsExpanded: true };
var itemRenderSize = tvItemIsExpanded ? tvItem.GetHeaderSize() : item.RenderSize;
+ this.acceptChildItem ??= tvItem != null;
if (this.VisualTargetOrientation == Orientation.Vertical)
{
- var currentYPos = e.GetPosition(item).Y;
+ var currentYPos = this.eventArgs.GetPosition(item).Y;
var targetHeight = itemRenderSize.Height;
var topGap = targetHeight * 0.25;
@@ -285,7 +305,7 @@ public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo,
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
}
- if (currentYPos > topGap && currentYPos < bottomGap)
+ if (this.AcceptChildItem && currentYPos > topGap && currentYPos < bottomGap)
{
if (tvItem != null)
{
@@ -300,7 +320,7 @@ public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo,
}
else
{
- var currentXPos = e.GetPosition(item).X;
+ var currentXPos = this.eventArgs.GetPosition(item).X;
var targetWidth = itemRenderSize.Width;
if (this.VisualTargetFlowDirection == FlowDirection.RightToLeft)
@@ -328,7 +348,7 @@ public DropInfo(object sender, DragEventArgs e, [CanBeNull] IDragInfo dragInfo,
}
}
- if (currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75)
+ if (this.AcceptChildItem && currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75)
{
if (tvItem != null)
{
diff --git a/src/GongSolutions.WPF.DragDrop/IDropInfo.cs b/src/GongSolutions.WPF.DragDrop/IDropInfo.cs
index 37d5b66b..ad8916ae 100644
--- a/src/GongSolutions.WPF.DragDrop/IDropInfo.cs
+++ b/src/GongSolutions.WPF.DragDrop/IDropInfo.cs
@@ -154,5 +154,13 @@ public interface IDropInfo
/// Gets the current mode of the underlying routed event.
///
EventType EventType { get; }
+
+ ///
+ /// Indicates if the drop target can accept the dragged data as a child item (applies to tree view items).
+ ///
+ ///
+ /// Changing this value will update other properties.
+ ///
+ bool AcceptChildItem { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Showcase/Models/FilesDropHandler.cs b/src/Showcase/Models/FilesDropHandler.cs
new file mode 100644
index 00000000..6b52389f
--- /dev/null
+++ b/src/Showcase/Models/FilesDropHandler.cs
@@ -0,0 +1,15 @@
+namespace Showcase.WPF.DragDrop.Models;
+
+using GongSolutions.Wpf.DragDrop;
+using MahApps.Metro.IconPacks;
+
+public class FilesDropHandler : DefaultDropHandler
+{
+ public override void DragOver(IDropInfo dropInfo)
+ {
+ if (dropInfo is DropInfo { TargetItem: not TreeNode { Icon: PackIconMaterialKind.Folder } } typedDropInfo)
+ typedDropInfo.AcceptChildItem = false;
+
+ base.DragOver(dropInfo);
+ }
+}
\ No newline at end of file
diff --git a/src/Showcase/Models/SampleData.cs b/src/Showcase/Models/SampleData.cs
index 4b1a12f4..322c0f66 100644
--- a/src/Showcase/Models/SampleData.cs
+++ b/src/Showcase/Models/SampleData.cs
@@ -5,6 +5,8 @@
namespace Showcase.WPF.DragDrop.Models
{
+ using MahApps.Metro.IconPacks;
+
public class SampleData
{
public SampleData()
@@ -37,15 +39,19 @@ public SampleData()
for (int r = 1; r <= 6; r++)
{
var root = new TreeNode($"Root {r}");
+ var folder = new TreeNode($"Folder {r}") { Icon = PackIconMaterialKind.Folder };
for (var i = 0; i < ((r % 2) == 0 ? 8 : 3); ++i)
{
root.Children.Add(new TreeNode($"Item {i + 10 * r}"));
+ folder.Children.Add(new TreeNode($"File {i + 10 * r}") { Icon = PackIconMaterialKind.File });
}
this.TreeCollection1.Add(root);
+ this.TreeCollectionFiles.Add(folder);
if (r == 2)
{
root.IsExpanded = true;
+ folder.IsExpanded = true;
}
}
@@ -85,6 +91,10 @@ public SampleData()
public ObservableCollection TreeCollection2 { get; set; } = new ObservableCollection();
+ public ObservableCollection TreeCollectionFiles { get; set; } = new ObservableCollection();
+
+ public FilesDropHandler FilesDropHandler { get; set; } = new FilesDropHandler();
+
public GroupedDropHandler GroupedDropHandler { get; set; } = new GroupedDropHandler();
public ObservableCollection GroupedCollection { get; set; } = new ObservableCollection();
diff --git a/src/Showcase/Models/TreeNode.cs b/src/Showcase/Models/TreeNode.cs
index ceaa7210..a7b8204b 100644
--- a/src/Showcase/Models/TreeNode.cs
+++ b/src/Showcase/Models/TreeNode.cs
@@ -3,11 +3,13 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
+using MahApps.Metro.IconPacks;
namespace Showcase.WPF.DragDrop.Models
{
public class TreeNode : INotifyPropertyChanged, ICloneable
{
+ private PackIconMaterialKind _icon;
private string _caption;
private ObservableCollection _children;
private bool _isCloned;
@@ -19,6 +21,17 @@ public TreeNode(string caption)
this.Children = new ObservableCollection();
}
+ public PackIconMaterialKind Icon
+ {
+ get => this._icon;
+ set
+ {
+ if (value == this._icon) return;
+ this._icon = value;
+ this.OnPropertyChanged();
+ }
+ }
+
public string Caption
{
get => this._caption;
diff --git a/src/Showcase/Views/TreeViewSamples.xaml b/src/Showcase/Views/TreeViewSamples.xaml
index 9d3dfe50..7ec38ecf 100644
--- a/src/Showcase/Views/TreeViewSamples.xaml
+++ b/src/Showcase/Views/TreeViewSamples.xaml
@@ -6,6 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
xmlns:views="clr-namespace:Showcase.WPF.DragDrop.Views"
+ xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
@@ -146,6 +147,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+