Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DropTarget hint and DropHighlightAdorner background brush #489

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,5 @@ Does your company use `GongSolutions.WPF.DragDrop`? Ask your manager or marketi
![screenshot04](./screenshots/2016-09-03_00h53_21.png)

![gif02](./screenshots/DragDropSample01.gif)

![gif03](./screenshots/DragHint-Demo.gif)
Binary file added screenshots/DragHint-Demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/GongSolutions.WPF.DragDrop/DefaultDropHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public static bool CanAcceptData(IDropInfo dropInfo)

public static IEnumerable ExtractData(object data)
{
if (data == null)
{
return Enumerable.Empty<object>();
}

if (data is IEnumerable enumerable and not string)
{
return enumerable;
Expand Down Expand Up @@ -200,6 +205,11 @@ protected static bool IsChildOf(UIElement targetItem, UIElement sourceItem)

protected static bool TestCompatibleTypes(IEnumerable target, object data)
{
if (data == null)
{
return false;
}

bool InterfaceFilter(Type t, object o) => (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));

var enumerableInterfaces = target.GetType().FindInterfaces(InterfaceFilter, null);
Expand All @@ -217,6 +227,11 @@ protected static bool TestCompatibleTypes(IEnumerable target, object data)
}
}

public virtual void DropHint(IDropHintInfo dropHintInfo)
{
dropHintInfo.DropTargetHintAdorner = DropTargetAdorners.Hint;
}

#if !NETCOREAPP3_1_OR_GREATER
/// <inheritdoc />
public void DragEnter(IDropInfo dropInfo)
Expand All @@ -234,6 +249,15 @@ public virtual void DragOver(IDropInfo dropInfo)
dropInfo.Effects = copyData ? DragDropEffects.Copy : DragDropEffects.Move;
var isTreeViewItem = dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.TargetItemCenter) && dropInfo.VisualTargetItem is TreeViewItem;
dropInfo.DropTargetAdorner = isTreeViewItem ? DropTargetAdorners.Highlight : DropTargetAdorners.Insert;

dropInfo.DropTargetHintState = DropHintState.Active;
dropInfo.DropTargetHintAdorner = DropTargetAdorners.Hint;
}
else
{
dropInfo.Effects = DragDropEffects.None;
dropInfo.DropTargetHintAdorner = DropTargetAdorners.Hint;
dropInfo.DropTargetHintState = DropHintState.Error;
}
}

Expand Down
103 changes: 103 additions & 0 deletions src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,31 @@ public static void SetDropTargetAdornerBrush(DependencyObject element, Brush val
element.SetValue(DropTargetAdornerBrushProperty, value);
}

/// <summary>
/// Gets or sets the brush to use for the <see cref="DropTargetHighlightAdorner"/>.
/// </summary>
public static readonly DependencyProperty DropTargetHighlightBrushProperty = DependencyProperty.RegisterAttached(
"DropTargetHighlightBrush", typeof(Brush), typeof(DragDrop), new PropertyMetadata(default(Brush)));

/// <summary>Helper for setting <see cref="DropTargetHighlightBrushProperty"/> on <paramref name="element"/>.</summary>
/// <param name="element"><see cref="DependencyObject"/> to set <see cref="DropTargetHighlightBrushProperty"/> on.</param>
/// <param name="value">The brush to use for the background of <see cref="DropTargetHighlightAdorner"/>.</param>
/// <remarks>Sets the brush for the DropTargetAdorner.</remarks>
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static void SetDropTargetHighlightBrush(DependencyObject element, Brush value)
{
element.SetValue(DropTargetHighlightBrushProperty, value);
}

/// <summary>Helper for setting <see cref="DropTargetAdornerBrushProperty"/> on <paramref name="element"/>.</summary>
/// <param name="element"><see cref="DependencyObject"/> to set <see cref="DropTargetAdornerBrushProperty"/> on.</param>
/// <remarks>Sets the brush for the DropTargetAdorner.</remarks>
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static Brush GetDropTargetHighlightBrush(DependencyObject element)
{
return (Brush)element.GetValue(DropTargetHighlightBrushProperty);
}

/// <summary>
/// Gets or sets the pen for the DropTargetAdorner.
/// </summary>
Expand Down Expand Up @@ -720,6 +745,84 @@ public static void SetDragDropCopyKeyState(DependencyObject element, DragDropKey
element.SetValue(DragDropCopyKeyStateProperty, value);
}

/// <summary>
/// Data template for displaying drop hint.
/// </summary>
public static readonly DependencyProperty DropHintDataTemplateProperty = DependencyProperty.RegisterAttached(
"DropHintDataTemplate", typeof(DataTemplate), typeof(DragDrop));

/// <summary>
/// Helper method for setting the <see cref="DropHintDataTemplateProperty"/> on the given <paramref name="element"/>.
/// This property is used when <see cref="UseDropTargetHintProperty"/> is set to <c>true</c> to display hint overlay
/// </summary>
/// <param name="element">The element to set the drop hint template for</param>
/// <param name="value">The <see cref="DataTemplate"/> to display</param>
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static void SetDropHintDataTemplate(DependencyObject element, DataTemplate value)
{
element.SetValue(DropHintDataTemplateProperty, value);
}

[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static DataTemplate GetDropHintDataTemplate(DependencyObject element)
{
return (DataTemplate)element.GetValue(DropHintDataTemplateProperty);
}

/// <summary>
/// Get or set whether drop target hint is used to indicate where the user can drop.
/// </summary>
public static readonly DependencyProperty UseDropTargetHintProperty
= DependencyProperty.RegisterAttached("UseDropTargetHint",
typeof(bool),
typeof(DragDrop),
new PropertyMetadata(default(bool), OnUseDropTargetHintChanged));

/// <summary>Helper for setting <see cref="UseDropTargetHintProperty"/> on <paramref name="element"/>.</summary>
/// <param name="element"><see cref="DependencyObject"/> to set <see cref="UseDropTargetHintProperty"/> on.</param>
/// <param name="value">UseDropTargetHintProperty property value.</param>
/// <remarks>Sets whether the hint adorner should be displayed.</remarks>
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static void SetUseDropTargetHint(DependencyObject element, bool value)
{
element.SetValue(UseDropTargetHintProperty, value);
}

/// <summary>Helper for getting <see cref="UseDropTargetHintProperty"/> from <paramref name="element"/>.</summary>
/// <param name="element"><see cref="DependencyObject"/> to read <see cref="UseDropTargetHintProperty"/> from.</param>
/// <remarks>Gets whether if the default DragAdorner is used.</remarks>
/// <returns>UseDropTargetHintProperty property value.</returns>
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static bool GetUseDropTargetHint(DependencyObject element)
{
return (bool)element.GetValue(UseDropTargetHintProperty);
}

/// <summary>
/// Implements side effects for when the UseDropTargetHintProperty changes.
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void OnUseDropTargetHintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dropTarget = d as UIElement;
if (dropTarget == null)
{
return;
}

// Add or remove drop target from hint cache.
bool useDropTargetHint = (bool)e.NewValue;
if (useDropTargetHint)
{
DropHintHelpers.AddDropHintTarget(dropTarget);
}
else
{
DropHintHelpers.RemoveDropHintTarget(dropTarget);
}
}

/// <summary>
/// Gets or sets whether if the default DragAdorner should be use.
/// </summary>
Expand Down
44 changes: 39 additions & 5 deletions src/GongSolutions.WPF.DragDrop/DragDrop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ namespace GongSolutions.Wpf.DragDrop
{
public static partial class DragDrop
{
/// <summary>
/// Get the <see cref="DataTemplate"/> for the drop hint, or return the default template if not set.
/// </summary>
/// <param name="sender"></param>
/// <returns></returns>
internal static DataTemplate TryGetDropHintDataTemplate(UIElement sender)
{
if (sender == null)
{
return null;
}

return GetDropHintDataTemplate(sender) ?? DropHintHelpers.GetDefaultDropHintTemplate();
}

/// <summary>
/// Gets the drag handler from the drag info or from the sender, if the drag info is null
/// </summary>
Expand All @@ -33,7 +48,7 @@ private static IDragSource TryGetDragHandler(IDragInfo dragInfo, UIElement sende
/// <param name="dropInfo">the drop info object</param>
/// <param name="sender">the sender from an event, e.g. drag over</param>
/// <returns></returns>
private static IDropTarget TryGetDropHandler(IDropInfo dropInfo, UIElement sender)
internal static IDropTarget TryGetDropHandler(IDropInfo dropInfo, UIElement sender)
{
var dropHandler = (dropInfo?.VisualTarget != null ? GetDropHandler(dropInfo.VisualTarget) : null) ?? (sender != null ? GetDropHandler(sender) : null);

Expand All @@ -55,7 +70,7 @@ private static IDragInfoBuilder TryGetDragInfoBuilder(DependencyObject sender)
/// </summary>
/// <param name="sender">the sender from an event, e.g. drag over</param>
/// <returns></returns>
private static IDropInfoBuilder TryGetDropInfoBuilder(DependencyObject sender)
internal static IDropInfoBuilder TryGetDropInfoBuilder(DependencyObject sender)
{
return sender != null ? GetDropInfoBuilder(sender) : null;
}
Expand Down Expand Up @@ -650,13 +665,15 @@ private static void DoDragSourceMove(object sender, Func<IInputElement, Point> g
}
});

DropHintHelpers.OnDragStart(dragInfo);
var dragDropHandler = dragInfo.DragDropHandler ?? System.Windows.DragDrop.DoDragDrop;
var dragDropEffects = dragDropHandler(dragInfo.VisualSource, dataObject, dragInfo.Effects);
if (dragDropEffects == DragDropEffects.None)
{
dragHandler.DragCancelled();
}

DropHintHelpers.OnDropFinished();
dragHandler.DragDropOperationFinished(dragDropEffects, dragInfo);
}
catch (Exception ex)
Expand Down Expand Up @@ -685,6 +702,7 @@ private static void DragSourceOnQueryContinueDrag(object sender, QueryContinueDr
DragDropPreview = null;
DragDropEffectPreview = null;
DropTargetAdorner = null;
DropHintHelpers.OnDropFinished();
Mouse.OverrideCursor = null;
}
}
Expand Down Expand Up @@ -719,7 +737,14 @@ private static void OnRealTargetDragLeave(object sender, DragEventArgs e)
var dropInfo = dropInfoBuilder?.CreateDropInfo(sender, e, dragInfo, eventType) ?? new DropInfo(sender, e, dragInfo, eventType);
var dropHandler = TryGetDropHandler(dropInfo, sender as UIElement);

dropHandler?.DragLeave(dropInfo);
if(dropHandler != null)
{
dropHandler.DragLeave(dropInfo);
if(_dragInProgress)
{
DropHintHelpers.OnDragLeave(sender, dropHandler, dragInfo);
}
}

DragDropEffectPreview = null;
DropTargetAdorner = null;
Expand Down Expand Up @@ -764,6 +789,7 @@ private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventTy
}

dropHandler.DragOver(dropInfo);
DropHintHelpers.DragOver(sender, dropInfo);

if (dragInfo is not null)
{
Expand Down Expand Up @@ -835,6 +861,15 @@ private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventTy
}
}

if(adorner is DropTargetHighlightAdorner highlightAdorner)
{
var highlightBrush = GetDropTargetHighlightBrush(dropInfo.VisualTarget);
if (highlightBrush != null)
{
highlightAdorner.Background = highlightBrush;
}
}

adorner.DropInfo = dropInfo;
adorner.InvalidateVisual();
}
Expand Down Expand Up @@ -898,7 +933,7 @@ private static void DropTargetOnDrop(object sender, DragEventArgs e, EventType e
DragDropPreview = null;
DragDropEffectPreview = null;
DropTargetAdorner = null;

DropHintHelpers.OnDropFinished();
dropHandler.DragOver(dropInfo);

if (itemsSorter != null && dropInfo.Data is IEnumerable enumerable and not string)
Expand All @@ -908,7 +943,6 @@ private static void DropTargetOnDrop(object sender, DragEventArgs e, EventType e

dropHandler.Drop(dropInfo);
dragHandler.Dropped(dropInfo);

e.Effects = dropInfo.Effects;
e.Handled = !dropInfo.NotHandled;

Expand Down
58 changes: 58 additions & 0 deletions src/GongSolutions.WPF.DragDrop/DropHintData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace GongSolutions.Wpf.DragDrop;

using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;

/// <summary>
/// Data presented in drop hint adorner.
/// </summary>
public class DropHintData : INotifyPropertyChanged
{
private DropHintState hintState;
private string hintText;

public DropHintData(DropHintState hintState, string hintText)
{
this.HintState = hintState;
this.HintText = hintText;
}

/// <summary>
/// The hint text to display to the user. See <see cref="IDropInfo.DropHintText"/>
/// and <see cref="IDropHintInfo.DropHintText"/>.
/// </summary>
public string HintText
{
get => this.hintText;
set
{
if (value == this.hintText) return;
this.hintText = value;
this.OnPropertyChanged();
}
}

/// <summary>
/// The hint state to display different colors for hints. See <see cref="IDropInfo.DropTargetHintState"/>
/// and <see cref="IDropHintInfo.DropTargetHintState"/>.
/// </summary>
public DropHintState HintState
{
get => this.hintState;
set
{
if (value == this.hintState) return;
this.hintState = value;
this.OnPropertyChanged();
}
}

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Loading
Loading