mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-30 13:13:24 +08:00
项目结构调整
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// The default drag handler for GongSolutions.Wpf.DragDrop.
|
||||
/// </summary>
|
||||
public class DefaultDragHandler : IDragSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Queries whether a drag can be started.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">Information about the drag.</param>
|
||||
/// <remarks>
|
||||
/// To allow a drag to be started, the <see cref="DragInfo.Effects" /> property on <paramref name="dragInfo" />
|
||||
/// should be set to a value other than <see cref="DragDropEffects.None" />.
|
||||
/// </remarks>
|
||||
public virtual void StartDrag(IDragInfo dragInfo)
|
||||
{
|
||||
var items = TypeUtilities.CreateDynamicallyTypedList(dragInfo.SourceItems).Cast<object>().ToList();
|
||||
if (items.Count > 1)
|
||||
{
|
||||
dragInfo.Data = items;
|
||||
}
|
||||
else
|
||||
{
|
||||
// special case: if the single item is an enumerable then we can not drop it as single item
|
||||
var singleItem = items.FirstOrDefault();
|
||||
if (singleItem is IEnumerable && !(singleItem is string))
|
||||
{
|
||||
dragInfo.Data = items;
|
||||
}
|
||||
else
|
||||
{
|
||||
dragInfo.Data = singleItem;
|
||||
}
|
||||
}
|
||||
|
||||
dragInfo.Effects = dragInfo.Data != null ? DragDropEffects.Copy | DragDropEffects.Move : DragDropEffects.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance [can start drag] the specified drag information.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">The drag information.</param>
|
||||
/// <returns></returns>
|
||||
public virtual bool CanStartDrag(IDragInfo dragInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drop has occurred.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">Information about the drop.</param>
|
||||
public virtual void Dropped(IDropInfo dropInfo)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drag and drop operation has finished.
|
||||
/// </summary>
|
||||
/// <param name="operationResult">The operation result.</param>
|
||||
/// <param name="dragInfo">The drag information.</param>
|
||||
public virtual void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo)
|
||||
{
|
||||
// nothing here
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drag has been aborted.
|
||||
/// </summary>
|
||||
public virtual void DragCancelled()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that an exception has occurred upon dragging.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception that occurrred.</param>
|
||||
/// <returns>
|
||||
/// Boolean indicating whether the exception is handled in the drag handler.
|
||||
/// False will rethrow the exception.
|
||||
/// </returns>
|
||||
public virtual bool TryCatchOccurredException(Exception exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// A default insertion drop handler for the most common usages
|
||||
/// </summary>
|
||||
public class DefaultDropHandler : IDropTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Test the specified drop information for the right data.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">The drop information.</param>
|
||||
public static bool CanAcceptData(IDropInfo dropInfo)
|
||||
{
|
||||
if (dropInfo == null || dropInfo.DragInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dropInfo.IsSameDragDropContextAsSource)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// do not drop on itself
|
||||
var isTreeViewItem = dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.TargetItemCenter)
|
||||
&& dropInfo.VisualTargetItem is TreeViewItem;
|
||||
if (isTreeViewItem && dropInfo.VisualTargetItem == dropInfo.DragInfo.VisualSourceItem)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dropInfo.DragInfo.SourceCollection == dropInfo.TargetCollection)
|
||||
{
|
||||
var targetList = dropInfo.TargetCollection.TryGetList();
|
||||
return targetList != null;
|
||||
}
|
||||
// else if (dropInfo.DragInfo.SourceCollection is ItemCollection) {
|
||||
// return false;
|
||||
// }
|
||||
else if (dropInfo.TargetCollection == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TestCompatibleTypes(dropInfo.TargetCollection, dropInfo.Data))
|
||||
{
|
||||
var isChildOf = IsChildOf(dropInfo.VisualTargetItem, dropInfo.DragInfo.VisualSourceItem);
|
||||
return !isChildOf;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable ExtractData(object data)
|
||||
{
|
||||
if (data is IEnumerable && !(data is string))
|
||||
{
|
||||
return (IEnumerable)data;
|
||||
}
|
||||
|
||||
return Enumerable.Repeat(data, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current selected items and selects the given items.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">The drop information.</param>
|
||||
/// <param name="items">The items which should be select.</param>
|
||||
/// <param name="applyTemplate">if set to <c>true</c> then for all items the ApplyTemplate will be invoked.</param>
|
||||
/// <param name="focusVisualTarget">if set to <c>true</c> the visual target will be focused.</param>
|
||||
/// <exception cref="System.ArgumentNullException"><paramref name="dropInfo" /> is <see langword="null" /></exception>
|
||||
/// <exception cref="System.ArgumentNullException"><paramref name="dropInfo" /> is <see langword="null" /></exception>
|
||||
public static void SelectDroppedItems([NotNull] IDropInfo dropInfo, [NotNull] IEnumerable items, bool applyTemplate = true, bool focusVisualTarget = true)
|
||||
{
|
||||
if (dropInfo == null) throw new ArgumentNullException(nameof(dropInfo));
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
var itemsControl = dropInfo.VisualTarget as ItemsControl;
|
||||
if (itemsControl != null)
|
||||
{
|
||||
var tvItem = dropInfo.VisualTargetItem as TreeViewItem;
|
||||
var tvItemIsExpanded = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded;
|
||||
|
||||
var itemsParent = tvItemIsExpanded ? tvItem : (dropInfo.VisualTargetItem != null ? ItemsControl.ItemsControlFromItemContainer(dropInfo.VisualTargetItem) : itemsControl);
|
||||
itemsParent = itemsParent ?? itemsControl;
|
||||
|
||||
itemsParent.ClearSelectedItems();
|
||||
|
||||
foreach (var obj in items)
|
||||
{
|
||||
if (applyTemplate)
|
||||
{
|
||||
// call ApplyTemplate for TabItem in TabControl to avoid this error:
|
||||
//
|
||||
// System.Windows.Data Error: 4 : Cannot find source for binding with reference
|
||||
var container = itemsParent.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
|
||||
container?.ApplyTemplate();
|
||||
}
|
||||
itemsParent.SetItemSelected(obj, true);
|
||||
}
|
||||
|
||||
if (focusVisualTarget)
|
||||
{
|
||||
itemsControl.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the data of the drag drop action should be copied otherwise moved.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">The DropInfo with a valid DragInfo.</param>
|
||||
public static bool ShouldCopyData(IDropInfo dropInfo)
|
||||
{
|
||||
// default should always the move action/effect
|
||||
if (dropInfo == null || dropInfo.DragInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var copyData = ((dropInfo.DragInfo.DragDropCopyKeyState != default(DragDropKeyStates)) && dropInfo.KeyStates.HasFlag(dropInfo.DragInfo.DragDropCopyKeyState))
|
||||
|| dropInfo.DragInfo.DragDropCopyKeyState.HasFlag(DragDropKeyStates.LeftMouseButton);
|
||||
copyData = copyData
|
||||
//&& (dropInfo.DragInfo.VisualSource != dropInfo.VisualTarget)
|
||||
&& !(dropInfo.DragInfo.SourceItem is HeaderedContentControl)
|
||||
&& !(dropInfo.DragInfo.SourceItem is HeaderedItemsControl)
|
||||
&& !(dropInfo.DragInfo.SourceItem is ListBoxItem);
|
||||
return copyData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the current drag state.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">Information about the drag.</param>
|
||||
/// <remarks>
|
||||
/// To allow a drop at the current drag position, the <see cref="DropInfo.Effects" /> property on
|
||||
/// <paramref name="dropInfo" /> should be set to a value other than <see cref="DragDropEffects.None" />
|
||||
/// and <see cref="DropInfo.Data" /> should be set to a non-null value.
|
||||
/// </remarks>
|
||||
public virtual void DragOver(IDropInfo dropInfo)
|
||||
{
|
||||
if (CanAcceptData(dropInfo))
|
||||
{
|
||||
dropInfo.Effects = ShouldCopyData(dropInfo) ? DragDropEffects.Copy : DragDropEffects.Move;
|
||||
var isTreeViewItem = dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.TargetItemCenter) && dropInfo.VisualTargetItem is TreeViewItem;
|
||||
dropInfo.DropTargetAdorner = isTreeViewItem ? DropTargetAdorners.Highlight : DropTargetAdorners.Insert;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a drop.
|
||||
/// </summary>
|
||||
/// <param name="dropInfo">Information about the drop.</param>
|
||||
public virtual void Drop(IDropInfo dropInfo)
|
||||
{
|
||||
if (dropInfo == null || dropInfo.DragInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var insertIndex = dropInfo.UnfilteredInsertIndex;
|
||||
|
||||
var itemsControl = dropInfo.VisualTarget as ItemsControl;
|
||||
if (itemsControl != null)
|
||||
{
|
||||
var editableItems = itemsControl.Items as IEditableCollectionView;
|
||||
if (editableItems != null)
|
||||
{
|
||||
var newItemPlaceholderPosition = editableItems.NewItemPlaceholderPosition;
|
||||
if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning && insertIndex == 0)
|
||||
{
|
||||
++insertIndex;
|
||||
}
|
||||
else if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtEnd && insertIndex == itemsControl.Items.Count)
|
||||
{
|
||||
--insertIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var destinationList = dropInfo.TargetCollection.TryGetList();
|
||||
var data = ExtractData(dropInfo.Data).OfType<object>().ToList();
|
||||
|
||||
var copyData = ShouldCopyData(dropInfo);
|
||||
if (!copyData)
|
||||
{
|
||||
var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList();
|
||||
if (sourceList != null)
|
||||
{
|
||||
foreach (var o in data)
|
||||
{
|
||||
var index = sourceList.IndexOf(o);
|
||||
if (index != -1)
|
||||
{
|
||||
sourceList.RemoveAt(index);
|
||||
// so, is the source list the destination list too ?
|
||||
if (destinationList != null && Equals(sourceList, destinationList) && index < insertIndex)
|
||||
{
|
||||
--insertIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (destinationList != null)
|
||||
{
|
||||
var objects2Insert = new List<object>();
|
||||
|
||||
// check for cloning
|
||||
var cloneData = dropInfo.Effects.HasFlag(DragDropEffects.Copy) || dropInfo.Effects.HasFlag(DragDropEffects.Link);
|
||||
foreach (var o in data)
|
||||
{
|
||||
var obj2Insert = o;
|
||||
if (cloneData)
|
||||
{
|
||||
var cloneable = o as ICloneable;
|
||||
if (cloneable != null)
|
||||
{
|
||||
obj2Insert = cloneable.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
objects2Insert.Add(obj2Insert);
|
||||
destinationList.Insert(insertIndex++, obj2Insert);
|
||||
}
|
||||
|
||||
var selectDroppedItems = itemsControl is TabControl || (itemsControl != null && DragDrop.GetSelectDroppedItems(itemsControl));
|
||||
if (selectDroppedItems)
|
||||
{
|
||||
SelectDroppedItems(dropInfo, objects2Insert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool IsChildOf(UIElement targetItem, UIElement sourceItem)
|
||||
{
|
||||
var parent = ItemsControl.ItemsControlFromItemContainer(targetItem);
|
||||
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent == sourceItem)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = ItemsControl.ItemsControlFromItemContainer(parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static bool TestCompatibleTypes(IEnumerable target, object data)
|
||||
{
|
||||
TypeFilter filter = (t, o) => { return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); };
|
||||
|
||||
var enumerableInterfaces = target.GetType().FindInterfaces(filter, null);
|
||||
var enumerableTypes = from i in enumerableInterfaces
|
||||
select i.GetGenericArguments().Single();
|
||||
|
||||
if (enumerableTypes.Count() > 0)
|
||||
{
|
||||
var dataType = TypeUtilities.GetCommonBaseClass(ExtractData(data));
|
||||
return enumerableTypes.Any(t => t.IsAssignableFrom(dataType));
|
||||
}
|
||||
else
|
||||
{
|
||||
return target is IList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!-- NuGet -->
|
||||
<PropertyGroup>
|
||||
<Authors>Jan Karger, Steven Kirk, mitchell.jon</Authors>
|
||||
<PackageId>gong-wpf-dragdrop</PackageId>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageProjectUrl>https://github.com/punker76/gong-wpf-dragdrop</PackageProjectUrl>
|
||||
<PackageIconUrl>https://raw.github.com/punker76/gong-wpf-dragdrop/master/GongSolutions.Wpf.DragDrop.png</PackageIconUrl>
|
||||
<PackageTags>WPF;Windows;UI;XAML;Toolkit;Library;.NET</PackageTags>
|
||||
<PackageReleaseNotes>https://github.com/punker76/gong-wpf-dragdrop/releases</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/punker76/gong-wpf-dragdrop.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Sign assembly -->
|
||||
<PropertyGroup>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\gong.public.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</Project>
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Windows.Documents;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
internal class DragAdorner : Adorner
|
||||
{
|
||||
public DragAdorner(UIElement adornedElement, UIElement adornment, Point translation, DragDropEffects effects = DragDropEffects.None)
|
||||
: base(adornedElement)
|
||||
{
|
||||
this._translation = translation;
|
||||
this.m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
|
||||
this.m_AdornerLayer.Add(this);
|
||||
this.m_Adornment = adornment;
|
||||
this.IsHitTestVisible = false;
|
||||
this.Effects = effects;
|
||||
}
|
||||
|
||||
public DragDropEffects Effects { get; private set; }
|
||||
|
||||
public Point MousePosition
|
||||
{
|
||||
get { return this.m_MousePosition; }
|
||||
set
|
||||
{
|
||||
if (this.m_MousePosition != value)
|
||||
{
|
||||
this.m_MousePosition = value;
|
||||
this.m_AdornerLayer.Update(this.AdornedElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Detatch()
|
||||
{
|
||||
this.m_AdornerLayer.Remove(this);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
this.m_Adornment.Arrange(new Rect(finalSize));
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
|
||||
{
|
||||
var result = new GeneralTransformGroup();
|
||||
result.Children.Add(base.GetDesiredTransform(transform));
|
||||
result.Children.Add(new TranslateTransform(this.MousePosition.X + this._translation.X, this.MousePosition.Y + this._translation.Y));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
return this.m_Adornment;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
this.m_Adornment.Measure(constraint);
|
||||
return this.m_Adornment.DesiredSize;
|
||||
}
|
||||
|
||||
protected override int VisualChildrenCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
private readonly AdornerLayer m_AdornerLayer;
|
||||
private readonly UIElement m_Adornment;
|
||||
private Point m_MousePosition;
|
||||
private Point _translation;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,738 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GongSolutions.Wpf.DragDrop.Icons;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public static partial class DragDrop
|
||||
{
|
||||
private static void CreateDragAdorner(DropInfo dropInfo)
|
||||
{
|
||||
var dragInfo = dropInfo.DragInfo;
|
||||
var template = GetDropAdornerTemplate(dropInfo.VisualTarget) ?? GetDragAdornerTemplate(dragInfo.VisualSource);
|
||||
var templateSelector = GetDropAdornerTemplateSelector(dropInfo.VisualTarget) ?? GetDragAdornerTemplateSelector(dragInfo.VisualSource);
|
||||
|
||||
UIElement adornment = null;
|
||||
|
||||
var useDefaultDragAdorner = template == null && templateSelector == null && GetUseDefaultDragAdorner(dragInfo.VisualSource);
|
||||
var useVisualSourceItemSizeForDragAdorner = GetUseVisualSourceItemSizeForDragAdorner(dragInfo.VisualSource);
|
||||
|
||||
if (useDefaultDragAdorner)
|
||||
{
|
||||
template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection);
|
||||
}
|
||||
|
||||
if (template != null || templateSelector != null)
|
||||
{
|
||||
if (dragInfo.Data is IEnumerable && !(dragInfo.Data is string))
|
||||
{
|
||||
if (!useDefaultDragAdorner && ((IEnumerable)dragInfo.Data).Cast<object>().Count() <= 10)
|
||||
{
|
||||
var itemsControl = new ItemsControl();
|
||||
itemsControl.ItemsSource = (IEnumerable)dragInfo.Data;
|
||||
itemsControl.ItemTemplate = template;
|
||||
itemsControl.ItemTemplateSelector = templateSelector;
|
||||
itemsControl.Tag = dragInfo;
|
||||
|
||||
if (useVisualSourceItemSizeForDragAdorner)
|
||||
{
|
||||
var bounds = VisualTreeHelper.GetDescendantBounds(dragInfo.VisualSourceItem);
|
||||
itemsControl.SetValue(FrameworkElement.MinWidthProperty, bounds.Width);
|
||||
}
|
||||
|
||||
// The ItemsControl doesn't display unless we create a grid to contain it.
|
||||
// Not quite sure why we need this...
|
||||
var grid = new Grid();
|
||||
grid.Children.Add(itemsControl);
|
||||
adornment = grid;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var contentPresenter = new ContentPresenter();
|
||||
contentPresenter.Content = dragInfo.Data;
|
||||
contentPresenter.ContentTemplate = template;
|
||||
contentPresenter.ContentTemplateSelector = templateSelector;
|
||||
contentPresenter.Tag = dragInfo;
|
||||
|
||||
if (useVisualSourceItemSizeForDragAdorner)
|
||||
{
|
||||
var bounds = VisualTreeHelper.GetDescendantBounds(dragInfo.VisualSourceItem);
|
||||
contentPresenter.SetValue(FrameworkElement.MinWidthProperty, bounds.Width);
|
||||
contentPresenter.SetValue(FrameworkElement.MinHeightProperty, bounds.Height);
|
||||
}
|
||||
|
||||
adornment = contentPresenter;
|
||||
}
|
||||
}
|
||||
|
||||
if (adornment != null)
|
||||
{
|
||||
if (useDefaultDragAdorner)
|
||||
{
|
||||
adornment.SetCurrentValue(UIElement.OpacityProperty, GetDefaultDragAdornerOpacity(dragInfo.VisualSource));
|
||||
}
|
||||
|
||||
var rootElement = RootElementFinder.FindRoot(dropInfo.VisualTarget ?? dragInfo.VisualSource);
|
||||
DragAdorner = new DragAdorner(rootElement, adornment, GetDragAdornerTranslation(dragInfo.VisualSource));
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateEffectAdorner(DropInfo dropInfo)
|
||||
{
|
||||
var dragInfo = m_DragInfo;
|
||||
var template = GetEffectAdornerTemplate(dragInfo.VisualSource, dropInfo.Effects, dropInfo.DestinationText, dropInfo.EffectText);
|
||||
|
||||
if (template != null)
|
||||
{
|
||||
var rootElement = RootElementFinder.FindRoot(dropInfo.VisualTarget ?? dragInfo.VisualSource);
|
||||
|
||||
var adornment = new ContentPresenter();
|
||||
adornment.Content = dragInfo.Data;
|
||||
adornment.ContentTemplate = template;
|
||||
|
||||
EffectAdorner = new DragAdorner(rootElement, adornment, GetEffectAdornerTranslation(dragInfo.VisualSource), dropInfo.Effects);
|
||||
}
|
||||
}
|
||||
|
||||
private static DataTemplate GetEffectAdornerTemplate(UIElement target, DragDropEffects effect, string destinationText, string effectText = null)
|
||||
{
|
||||
switch (effect)
|
||||
{
|
||||
case DragDropEffects.All:
|
||||
// TODO: Add default template for EffectAll
|
||||
return GetEffectAllAdornerTemplate(target);
|
||||
case DragDropEffects.Copy:
|
||||
return GetEffectCopyAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectCopy, effectText == null ? "Copy to" : effectText, destinationText);
|
||||
case DragDropEffects.Link:
|
||||
return GetEffectLinkAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectLink, effectText == null ? "Link to" : effectText, destinationText);
|
||||
case DragDropEffects.Move:
|
||||
return GetEffectMoveAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectMove, effectText == null ? "Move to" : effectText, destinationText);
|
||||
case DragDropEffects.None:
|
||||
return GetEffectNoneAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectNone, effectText == null ? "None" : effectText, destinationText);
|
||||
case DragDropEffects.Scroll:
|
||||
// TODO: Add default template EffectScroll
|
||||
return GetEffectScrollAdornerTemplate(target);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static DataTemplate CreateDefaultEffectDataTemplate(UIElement target, BitmapImage effectIcon, string effectText, string destinationText)
|
||||
{
|
||||
if (!GetUseDefaultEffectDataTemplate(target))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fontSize = SystemFonts.MessageFontSize; // before 11d
|
||||
|
||||
// The icon
|
||||
var imageFactory = new FrameworkElementFactory(typeof(Image));
|
||||
imageFactory.SetValue(Image.SourceProperty, effectIcon);
|
||||
imageFactory.SetValue(FrameworkElement.HeightProperty, 12d);
|
||||
imageFactory.SetValue(FrameworkElement.WidthProperty, 12d);
|
||||
|
||||
// Only the icon for no effect
|
||||
if (Equals(effectIcon, GongSolutions.Wpf.DragDrop.Icons.IconFactory.EffectNone))
|
||||
{
|
||||
return new DataTemplate { VisualTree = imageFactory };
|
||||
}
|
||||
|
||||
// Some margin for the icon
|
||||
imageFactory.SetValue(FrameworkElement.MarginProperty, new Thickness(0, 0, 3, 0));
|
||||
imageFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
|
||||
|
||||
// Add effect text
|
||||
var effectTextBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
|
||||
effectTextBlockFactory.SetValue(TextBlock.TextProperty, effectText);
|
||||
effectTextBlockFactory.SetValue(TextBlock.FontSizeProperty, fontSize);
|
||||
effectTextBlockFactory.SetValue(TextBlock.ForegroundProperty, Brushes.Blue);
|
||||
effectTextBlockFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
|
||||
|
||||
// Add destination text
|
||||
var destinationTextBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
|
||||
destinationTextBlockFactory.SetValue(TextBlock.TextProperty, destinationText);
|
||||
destinationTextBlockFactory.SetValue(TextBlock.FontSizeProperty, fontSize);
|
||||
destinationTextBlockFactory.SetValue(TextBlock.ForegroundProperty, Brushes.DarkBlue);
|
||||
destinationTextBlockFactory.SetValue(TextBlock.MarginProperty, new Thickness(3, 0, 0, 0));
|
||||
destinationTextBlockFactory.SetValue(TextBlock.FontWeightProperty, FontWeights.DemiBold);
|
||||
destinationTextBlockFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
|
||||
|
||||
// Create containing panel
|
||||
var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
|
||||
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
|
||||
stackPanelFactory.SetValue(FrameworkElement.MarginProperty, new Thickness(2));
|
||||
stackPanelFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
|
||||
stackPanelFactory.AppendChild(imageFactory);
|
||||
stackPanelFactory.AppendChild(effectTextBlockFactory);
|
||||
stackPanelFactory.AppendChild(destinationTextBlockFactory);
|
||||
|
||||
// Add border
|
||||
var borderFactory = new FrameworkElementFactory(typeof(Border));
|
||||
var stopCollection = new GradientStopCollection
|
||||
{
|
||||
new GradientStop(Colors.White, 0.0),
|
||||
new GradientStop(Colors.AliceBlue, 1.0)
|
||||
};
|
||||
var gradientBrush = new LinearGradientBrush(stopCollection)
|
||||
{
|
||||
StartPoint = new Point(0, 0),
|
||||
EndPoint = new Point(0, 1)
|
||||
};
|
||||
borderFactory.SetValue(Panel.BackgroundProperty, gradientBrush);
|
||||
borderFactory.SetValue(Border.BorderBrushProperty, Brushes.DimGray);
|
||||
borderFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(3));
|
||||
borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
|
||||
borderFactory.SetValue(Border.SnapsToDevicePixelsProperty, true);
|
||||
borderFactory.SetValue(TextOptions.TextFormattingModeProperty, TextFormattingMode.Display);
|
||||
borderFactory.SetValue(TextOptions.TextRenderingModeProperty, TextRenderingMode.ClearType);
|
||||
borderFactory.SetValue(TextOptions.TextHintingModeProperty, TextHintingMode.Fixed);
|
||||
borderFactory.AppendChild(stackPanelFactory);
|
||||
|
||||
// Finally add content to template
|
||||
return new DataTemplate { VisualTree = borderFactory };
|
||||
}
|
||||
|
||||
private static void Scroll(DropInfo dropInfo, DragEventArgs e)
|
||||
{
|
||||
if (dropInfo == null || dropInfo.TargetScrollViewer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollViewer = dropInfo.TargetScrollViewer;
|
||||
var scrollingMode = dropInfo.TargetScrollingMode;
|
||||
|
||||
var position = e.GetPosition(scrollViewer);
|
||||
var scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2);
|
||||
|
||||
if (scrollingMode == ScrollingMode.Both || scrollingMode == ScrollingMode.HorizontalOnly)
|
||||
{
|
||||
if (position.X >= scrollViewer.ActualWidth - scrollMargin && scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
|
||||
{
|
||||
scrollViewer.LineRight();
|
||||
}
|
||||
else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0)
|
||||
{
|
||||
scrollViewer.LineLeft();
|
||||
}
|
||||
}
|
||||
|
||||
if (scrollingMode == ScrollingMode.Both || scrollingMode == ScrollingMode.VerticalOnly)
|
||||
{
|
||||
if (position.Y >= scrollViewer.ActualHeight - scrollMargin && scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
|
||||
{
|
||||
scrollViewer.LineDown();
|
||||
}
|
||||
else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0)
|
||||
{
|
||||
scrollViewer.LineUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag handler from the drag info or from the sender, if the drag info is null
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">the drag info object</param>
|
||||
/// <param name="sender">the sender from an event, e.g. mouse down, mouse move</param>
|
||||
/// <returns></returns>
|
||||
private static IDragSource TryGetDragHandler(DragInfo dragInfo, UIElement sender)
|
||||
{
|
||||
IDragSource dragHandler = null;
|
||||
if (dragInfo != null && dragInfo.VisualSource != null)
|
||||
{
|
||||
dragHandler = GetDragHandler(dragInfo.VisualSource);
|
||||
}
|
||||
if (dragHandler == null && sender != null)
|
||||
{
|
||||
dragHandler = GetDragHandler(sender);
|
||||
}
|
||||
return dragHandler ?? DefaultDragHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drop handler from the drop info or from the sender, if the drop info is null
|
||||
/// </summary>
|
||||
/// <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(DropInfo dropInfo, UIElement sender)
|
||||
{
|
||||
IDropTarget dropHandler = null;
|
||||
if (dropInfo != null && dropInfo.VisualTarget != null)
|
||||
{
|
||||
dropHandler = GetDropHandler(dropInfo.VisualTarget);
|
||||
}
|
||||
if (dropHandler == null && sender != null)
|
||||
{
|
||||
dropHandler = GetDropHandler(sender);
|
||||
}
|
||||
return dropHandler ?? DefaultDropHandler;
|
||||
}
|
||||
|
||||
private static void DragSourceOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DoMouseButtonDown(sender, e);
|
||||
}
|
||||
|
||||
private static void DragSourceOnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DoMouseButtonDown(sender, e);
|
||||
}
|
||||
|
||||
private static void DoMouseButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
m_DragInfo = null;
|
||||
|
||||
// Ignore the click if clickCount != 1 or the user has clicked on a scrollbar.
|
||||
var elementPosition = e.GetPosition((IInputElement)sender);
|
||||
if (e.ClickCount != 1
|
||||
|| (sender as UIElement).IsDragSourceIgnored()
|
||||
|| (e.Source as UIElement).IsDragSourceIgnored()
|
||||
|| (e.OriginalSource as UIElement).IsDragSourceIgnored()
|
||||
|| (sender is TabControl) && !HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4Type<RangeBase>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4Type<TextBoxBase>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4Type<PasswordBox>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4Type<ComboBox>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4GridViewColumnHeader(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4DataGridTypes(sender, elementPosition)
|
||||
|| HitTestUtilities.IsNotPartOfSender(sender, e))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dragInfo = new DragInfo(sender, e);
|
||||
|
||||
if (dragInfo.VisualSource is ItemsControl control && control.CanSelectMultipleItems())
|
||||
{
|
||||
control.Focus();
|
||||
}
|
||||
|
||||
if (dragInfo.VisualSourceItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
|
||||
if (!dragHandler.CanStartDrag(dragInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the sender is a list box that allows multiple selections, ensure that clicking on an
|
||||
// already selected item does not change the selection, otherwise dragging multiple items
|
||||
// is made impossible.
|
||||
var itemsControl = sender as ItemsControl;
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0 && (Keyboard.Modifiers & ModifierKeys.Control) == 0 && dragInfo.VisualSourceItem != null && itemsControl != null && itemsControl.CanSelectMultipleItems())
|
||||
{
|
||||
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().ToList();
|
||||
if (selectedItems.Count > 1 && selectedItems.Contains(dragInfo.SourceItem))
|
||||
{
|
||||
m_ClickSupressItem = dragInfo.SourceItem;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_DragInfo = dragInfo;
|
||||
}
|
||||
|
||||
private static void DragSourceOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DoMouseButtonUp(sender, e);
|
||||
}
|
||||
|
||||
private static void DragSourceOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DoMouseButtonUp(sender, e);
|
||||
}
|
||||
|
||||
private static void DoMouseButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var elementPosition = e.GetPosition((IInputElement)sender);
|
||||
if ((sender is TabControl) && !HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition))
|
||||
{
|
||||
m_DragInfo = null;
|
||||
m_ClickSupressItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var dragInfo = m_DragInfo;
|
||||
|
||||
// If we prevented the control's default selection handling in DragSource_PreviewMouseLeftButtonDown
|
||||
// by setting 'e.Handled = true' and a drag was not initiated, manually set the selection here.
|
||||
var itemsControl = sender as ItemsControl;
|
||||
if (itemsControl != null && dragInfo != null && m_ClickSupressItem != null && m_ClickSupressItem == dragInfo.SourceItem)
|
||||
{
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
|
||||
{
|
||||
itemsControl.SetItemSelected(dragInfo.SourceItem, false);
|
||||
}
|
||||
else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
|
||||
{
|
||||
itemsControl.SetSelectedItem(dragInfo.SourceItem);
|
||||
}
|
||||
}
|
||||
|
||||
m_DragInfo = null;
|
||||
m_ClickSupressItem = null;
|
||||
}
|
||||
|
||||
private static void DragSourceOnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
var dragInfo = m_DragInfo;
|
||||
if (dragInfo != null && !m_DragInProgress)
|
||||
{
|
||||
// the start from the source
|
||||
var dragStart = dragInfo.DragStartPosition;
|
||||
|
||||
// do nothing if mouse left/right button is released or the pointer is captured
|
||||
if (dragInfo.MouseButton == MouseButton.Left && e.LeftButton == MouseButtonState.Released)
|
||||
{
|
||||
m_DragInfo = null;
|
||||
return;
|
||||
}
|
||||
if (DragDrop.GetCanDragWithMouseRightButton(dragInfo.VisualSource) && dragInfo.MouseButton == MouseButton.Right && e.RightButton == MouseButtonState.Released)
|
||||
{
|
||||
m_DragInfo = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// current mouse position
|
||||
var position = e.GetPosition((IInputElement)sender);
|
||||
|
||||
// prevent selection changing while drag operation
|
||||
dragInfo.VisualSource?.ReleaseMouseCapture();
|
||||
|
||||
// only if the sender is the source control and the mouse point differs from an offset
|
||||
if (dragInfo.VisualSource == sender
|
||||
&& (Math.Abs(position.X - dragStart.X) > DragDrop.GetMinimumHorizontalDragDistance(dragInfo.VisualSource) ||
|
||||
Math.Abs(position.Y - dragStart.Y) > DragDrop.GetMinimumVerticalDragDistance(dragInfo.VisualSource)))
|
||||
{
|
||||
dragInfo.RefreshSelectedItems(sender, e);
|
||||
|
||||
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
|
||||
if (dragHandler.CanStartDrag(dragInfo))
|
||||
{
|
||||
dragHandler.StartDrag(dragInfo);
|
||||
|
||||
if (dragInfo.Effects != DragDropEffects.None)
|
||||
{
|
||||
var dataObject = dragInfo.DataObject;
|
||||
|
||||
if (dataObject == null)
|
||||
{
|
||||
if (dragInfo.Data == null)
|
||||
{
|
||||
// it's bad if the Data is null, cause the DataObject constructor will raise an ArgumentNullException
|
||||
m_DragInfo = null; // maybe not necessary or should not set here to null
|
||||
return;
|
||||
}
|
||||
dataObject = new DataObject(dragInfo.DataFormat.Name, dragInfo.Data);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_DragInProgress = true;
|
||||
var dragDropHandler = dragInfo.DragDropHandler ?? System.Windows.DragDrop.DoDragDrop;
|
||||
var dragDropEffects = dragDropHandler(dragInfo.VisualSource, dataObject, dragInfo.Effects);
|
||||
if (dragDropEffects == DragDropEffects.None)
|
||||
{
|
||||
dragHandler.DragCancelled();
|
||||
}
|
||||
dragHandler.DragDropOperationFinished(dragDropEffects, dragInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!dragHandler.TryCatchOccurredException(ex))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_DragInProgress = false;
|
||||
m_DragInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DragSourceOnQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
|
||||
{
|
||||
if (e.Action == DragAction.Cancel || e.EscapePressed)
|
||||
{
|
||||
DragAdorner = null;
|
||||
EffectAdorner = null;
|
||||
DropTargetAdorner = null;
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DropTargetOnDragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDragOver(sender, e, EventType.Bubbled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnPreviewDragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDragOver(sender, e, EventType.Tunneled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnDragLeave(object sender, DragEventArgs e)
|
||||
{
|
||||
DragAdorner = null;
|
||||
EffectAdorner = null;
|
||||
DropTargetAdorner = null;
|
||||
}
|
||||
|
||||
private static void DropTargetOnDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDragOver(sender, e, EventType.Bubbled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnPreviewDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDragOver(sender, e, EventType.Tunneled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventType eventType)
|
||||
{
|
||||
var elementPosition = e.GetPosition((IInputElement)sender);
|
||||
|
||||
var dragInfo = m_DragInfo;
|
||||
var dropInfo = new DropInfo(sender, e, dragInfo, eventType);
|
||||
var dropHandler = TryGetDropHandler(dropInfo, sender as UIElement);
|
||||
var itemsControl = dropInfo.VisualTarget;
|
||||
|
||||
dropHandler.DragOver(dropInfo);
|
||||
|
||||
if (DragAdorner == null && dragInfo != null)
|
||||
{
|
||||
CreateDragAdorner(dropInfo);
|
||||
}
|
||||
|
||||
if (DragAdorner != null)
|
||||
{
|
||||
var tempAdornerPos = e.GetPosition(DragAdorner.AdornedElement);
|
||||
|
||||
if (tempAdornerPos.X >= 0 && tempAdornerPos.Y >= 0)
|
||||
{
|
||||
_adornerPos = tempAdornerPos;
|
||||
}
|
||||
|
||||
// Fixed the flickering adorner - Size changes to zero 'randomly'...?
|
||||
if (DragAdorner.RenderSize.Width > 0 && DragAdorner.RenderSize.Height > 0)
|
||||
{
|
||||
_adornerSize = DragAdorner.RenderSize;
|
||||
}
|
||||
|
||||
if (dragInfo != null)
|
||||
{
|
||||
// move the adorner
|
||||
var offsetX = _adornerSize.Width * -GetDragMouseAnchorPoint(dragInfo.VisualSource).X;
|
||||
var offsetY = _adornerSize.Height * -GetDragMouseAnchorPoint(dragInfo.VisualSource).Y;
|
||||
_adornerPos.Offset(offsetX, offsetY);
|
||||
var maxAdornerPosX = DragAdorner.AdornedElement.RenderSize.Width;
|
||||
var adornerPosRightX = (_adornerPos.X + _adornerSize.Width);
|
||||
if (adornerPosRightX > maxAdornerPosX)
|
||||
{
|
||||
_adornerPos.Offset(-adornerPosRightX + maxAdornerPosX, 0);
|
||||
}
|
||||
if (_adornerPos.Y < 0)
|
||||
{
|
||||
_adornerPos.Y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DragAdorner.MousePosition = _adornerPos;
|
||||
DragAdorner.InvalidateVisual();
|
||||
}
|
||||
|
||||
Scroll(dropInfo, e);
|
||||
|
||||
if (HitTestUtilities.HitTest4Type<ScrollBar>(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4GridViewColumnHeader(sender, elementPosition)
|
||||
|| HitTestUtilities.HitTest4DataGridTypesOnDragOver(sender, elementPosition))
|
||||
{
|
||||
e.Effects = DragDropEffects.None;
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the target is an ItemsControl then update the drop target adorner.
|
||||
if (itemsControl != null)
|
||||
{
|
||||
// Display the adorner in the control's ItemsPresenter. If there is no
|
||||
// ItemsPresenter provided by the style, try getting hold of a
|
||||
// ScrollContentPresenter and using that.
|
||||
UIElement adornedElement = null;
|
||||
if (itemsControl is TabControl)
|
||||
{
|
||||
adornedElement = itemsControl.GetVisualDescendent<TabPanel>();
|
||||
}
|
||||
else if (itemsControl is DataGrid || (itemsControl as ListView)?.View is GridView)
|
||||
{
|
||||
adornedElement = itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement ?? itemsControl.GetVisualDescendent<ItemsPresenter>() as UIElement ?? itemsControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
adornedElement = itemsControl.GetVisualDescendent<ItemsPresenter>() as UIElement ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement ?? itemsControl;
|
||||
}
|
||||
|
||||
if (adornedElement != null)
|
||||
{
|
||||
if (dropInfo.DropTargetAdorner == null)
|
||||
{
|
||||
DropTargetAdorner = null;
|
||||
}
|
||||
else if (!dropInfo.DropTargetAdorner.IsInstanceOfType(DropTargetAdorner))
|
||||
{
|
||||
DropTargetAdorner = DropTargetAdorner.Create(dropInfo.DropTargetAdorner, adornedElement, dropInfo);
|
||||
}
|
||||
|
||||
var adorner = DropTargetAdorner;
|
||||
if (adorner != null)
|
||||
{
|
||||
var adornerBrush = GetDropTargetAdornerBrush(dropInfo.VisualTarget);
|
||||
if (adornerBrush != null)
|
||||
{
|
||||
adorner.Pen.SetCurrentValue(Pen.BrushProperty, adornerBrush);
|
||||
}
|
||||
adorner.DropInfo = dropInfo;
|
||||
adorner.InvalidateVisual();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the drag effect adorner if there is one
|
||||
if (dragInfo != null && (EffectAdorner == null || EffectAdorner.Effects != dropInfo.Effects))
|
||||
{
|
||||
CreateEffectAdorner(dropInfo);
|
||||
}
|
||||
|
||||
if (EffectAdorner != null)
|
||||
{
|
||||
var adornerPos = e.GetPosition(EffectAdorner.AdornedElement);
|
||||
//adornerPos.Offset(20, 20);
|
||||
EffectAdorner.MousePosition = adornerPos;
|
||||
EffectAdorner.InvalidateVisual();
|
||||
}
|
||||
|
||||
e.Effects = dropInfo.Effects;
|
||||
e.Handled = !dropInfo.NotHandled;
|
||||
|
||||
if (!dropInfo.IsSameDragDropContextAsSource)
|
||||
{
|
||||
e.Effects = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DropTargetOnDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDrop(sender, e, EventType.Bubbled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnPreviewDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
DropTargetOnDrop(sender, e, EventType.Tunneled);
|
||||
}
|
||||
|
||||
private static void DropTargetOnDrop(object sender, DragEventArgs e, EventType eventType)
|
||||
{
|
||||
var dragInfo = m_DragInfo;
|
||||
var dropInfo = new DropInfo(sender, e, dragInfo, eventType);
|
||||
var dropHandler = TryGetDropHandler(dropInfo, sender as UIElement);
|
||||
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
|
||||
|
||||
DragAdorner = null;
|
||||
EffectAdorner = null;
|
||||
DropTargetAdorner = null;
|
||||
|
||||
dropHandler.DragOver(dropInfo);
|
||||
dropHandler.Drop(dropInfo);
|
||||
dragHandler.Dropped(dropInfo);
|
||||
|
||||
e.Effects = dropInfo.Effects;
|
||||
e.Handled = !dropInfo.NotHandled;
|
||||
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
|
||||
private static void DropTargetOnGiveFeedback(object sender, GiveFeedbackEventArgs e)
|
||||
{
|
||||
if (EffectAdorner != null)
|
||||
{
|
||||
e.UseDefaultCursors = false;
|
||||
e.Handled = true;
|
||||
if (Mouse.OverrideCursor != Cursors.Arrow)
|
||||
{
|
||||
Mouse.OverrideCursor = Cursors.Arrow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
e.UseDefaultCursors = true;
|
||||
e.Handled = true;
|
||||
if (Mouse.OverrideCursor != null)
|
||||
{
|
||||
Mouse.OverrideCursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DragAdorner _DragAdorner;
|
||||
|
||||
private static DragAdorner DragAdorner
|
||||
{
|
||||
get { return _DragAdorner; }
|
||||
set
|
||||
{
|
||||
_DragAdorner?.Detatch();
|
||||
_DragAdorner = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static DragAdorner _EffectAdorner;
|
||||
|
||||
private static DragAdorner EffectAdorner
|
||||
{
|
||||
get { return _EffectAdorner; }
|
||||
set
|
||||
{
|
||||
_EffectAdorner?.Detatch();
|
||||
_EffectAdorner = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static DropTargetAdorner _DropTargetAdorner;
|
||||
|
||||
private static DropTargetAdorner DropTargetAdorner
|
||||
{
|
||||
get { return _DropTargetAdorner; }
|
||||
set
|
||||
{
|
||||
_DropTargetAdorner?.Detatch();
|
||||
_DropTargetAdorner = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static DragInfo m_DragInfo;
|
||||
private static bool m_DragInProgress;
|
||||
private static object m_ClickSupressItem;
|
||||
private static Point _adornerPos;
|
||||
private static Size _adornerSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds information about a the source of a drag drop operation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// The <see cref="DragInfo"/> class holds all of the framework's information about the source
|
||||
/// of a drag. It is used by <see cref="IDragSource.StartDrag"/> to determine whether a drag
|
||||
/// can start, and what the dragged data should be.
|
||||
/// </remarks>
|
||||
public class DragInfo : IDragInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DragInfo class.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="sender">
|
||||
/// The sender of the mouse event that initiated the drag.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="e">
|
||||
/// The mouse event that initiated the drag.
|
||||
/// </param>
|
||||
public DragInfo(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
this.Effects = DragDropEffects.None;
|
||||
this.MouseButton = e.ChangedButton;
|
||||
this.VisualSource = sender as UIElement;
|
||||
this.DragStartPosition = e.GetPosition(this.VisualSource);
|
||||
this.DragDropCopyKeyState = DragDrop.GetDragDropCopyKeyState(this.VisualSource);
|
||||
|
||||
var dataFormat = DragDrop.GetDataFormat(this.VisualSource);
|
||||
if (dataFormat != null)
|
||||
{
|
||||
this.DataFormat = dataFormat;
|
||||
}
|
||||
|
||||
var sourceElement = e.OriginalSource as UIElement;
|
||||
// If we can't cast object as a UIElement it might be a FrameworkContentElement, if so try and use its parent.
|
||||
if (sourceElement == null && e.OriginalSource is FrameworkContentElement)
|
||||
{
|
||||
sourceElement = ((FrameworkContentElement)e.OriginalSource).Parent as UIElement;
|
||||
}
|
||||
|
||||
if (sender is ItemsControl)
|
||||
{
|
||||
var itemsControl = (ItemsControl)sender;
|
||||
|
||||
this.SourceGroup = itemsControl.FindGroup(this.DragStartPosition);
|
||||
this.VisualSourceFlowDirection = itemsControl.GetItemsPanelFlowDirection();
|
||||
|
||||
UIElement item = null;
|
||||
if (sourceElement != null)
|
||||
{
|
||||
item = itemsControl.GetItemContainer(sourceElement);
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
if (DragDrop.GetDragDirectlySelectedOnly(this.VisualSource))
|
||||
{
|
||||
item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl));
|
||||
}
|
||||
else
|
||||
{
|
||||
item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl), itemsControl.GetItemsPanelOrientation());
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
// Remember the relative position of the item being dragged
|
||||
this.PositionInDraggedItem = e.GetPosition(item);
|
||||
|
||||
var itemParent = ItemsControl.ItemsControlFromItemContainer(item);
|
||||
|
||||
if (itemParent != null)
|
||||
{
|
||||
this.SourceCollection = itemParent.ItemsSource ?? itemParent.Items;
|
||||
if (itemParent != itemsControl)
|
||||
{
|
||||
var tvItem = item as TreeViewItem;
|
||||
if (tvItem != null)
|
||||
{
|
||||
var tv = tvItem.GetVisualAncestor<TreeView>();
|
||||
if (tv != null && tv != itemsControl && !tv.IsDragSource())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (itemsControl.ItemContainerGenerator.IndexFromContainer(itemParent) < 0 && !itemParent.IsDragSource())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.SourceIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
|
||||
this.SourceItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SourceIndex = -1;
|
||||
}
|
||||
|
||||
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().Where(i => i != CollectionView.NewItemPlaceholder).ToList();
|
||||
this.SourceItems = selectedItems;
|
||||
|
||||
// Some controls (I'm looking at you TreeView!) haven't updated their
|
||||
// SelectedItem by this point. Check to see if there 1 or less item in
|
||||
// the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
|
||||
//
|
||||
// The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
|
||||
if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
|
||||
{
|
||||
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
|
||||
}
|
||||
|
||||
this.VisualSourceItem = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SourceCollection = itemsControl.ItemsSource ?? itemsControl.Items;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SourceItem = (sender as FrameworkElement)?.DataContext;
|
||||
if (this.SourceItem != null)
|
||||
{
|
||||
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
|
||||
}
|
||||
this.VisualSourceItem = sourceElement;
|
||||
this.PositionInDraggedItem = sourceElement != null ? e.GetPosition(sourceElement) : this.DragStartPosition;
|
||||
}
|
||||
|
||||
if (this.SourceItems == null)
|
||||
{
|
||||
this.SourceItems = Enumerable.Empty<object>();
|
||||
}
|
||||
}
|
||||
|
||||
internal void RefreshSelectedItems(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (sender is ItemsControl)
|
||||
{
|
||||
var itemsControl = (ItemsControl)sender;
|
||||
|
||||
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().Where(i => i != CollectionView.NewItemPlaceholder).ToList();
|
||||
this.SourceItems = selectedItems;
|
||||
|
||||
// Some controls (I'm looking at you TreeView!) haven't updated their
|
||||
// SelectedItem by this point. Check to see if there 1 or less item in
|
||||
// the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
|
||||
//
|
||||
// The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
|
||||
if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
|
||||
{
|
||||
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data format which will be used for the drag and drop actions.
|
||||
/// </summary>
|
||||
/// <value>The data format.</value>
|
||||
public DataFormat DataFormat { get; set; } = DragDrop.DataFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the drag data.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set by a drag handler in order for a drag to start.
|
||||
/// </remarks>
|
||||
public object Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
|
||||
/// </summary>
|
||||
public Point DragStartPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point where the cursor was relative to the item being dragged when the drag was started.
|
||||
/// </summary>
|
||||
public Point PositionInDraggedItem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed effects for the drag.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order
|
||||
/// for a drag to start.
|
||||
/// </remarks>
|
||||
public DragDropEffects Effects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse button that initiated the drag.
|
||||
/// </summary>
|
||||
public MouseButton MouseButton { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection that the source ItemsControl is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
public IEnumerable SourceCollection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from where the item was dragged.
|
||||
/// </summary>
|
||||
/// <value>The index of the source.</value>
|
||||
public int SourceIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that a dragged item is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
public object SourceItem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
|
||||
/// </remarks>
|
||||
public IEnumerable SourceItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the group from a dragged item if the drag is currently from an ItemsControl with groups.
|
||||
/// </summary>
|
||||
public CollectionViewGroup SourceGroup { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that initiated the drag.
|
||||
/// </summary>
|
||||
public UIElement VisualSource { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item in an ItemsControl that started the drag.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initiated the drag is an ItemsControl, this property will hold the item
|
||||
/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
|
||||
/// will hold a ListBoxItem.
|
||||
/// </remarks>
|
||||
public UIElement VisualSourceItem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FlowDirection of the current drag source.
|
||||
/// </summary>
|
||||
public FlowDirection VisualSourceFlowDirection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IDataObject"/> which is used by the drag and drop operation. Set it to
|
||||
/// a custom instance if custom drag and drop behavior is needed.
|
||||
/// </summary>
|
||||
public object DataObject { get; set; }
|
||||
|
||||
/// <summary>Initiates a drag-and-drop operation.</summary>
|
||||
/// <returns>One of the <see cref="T:System.Windows.DragDropEffects" /> values that specifies the final effect that was performed during the drag-and-drop operation.</returns>
|
||||
public Func<DependencyObject, object, DragDropEffects, DragDropEffects> DragDropHandler { get; set; } = System.Windows.DragDrop.DoDragDrop;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag drop copy key state indicating the effect of the drag drop operation.
|
||||
/// </summary>
|
||||
public DragDropKeyStates DragDropCopyKeyState { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds information about a the target of a drag drop operation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// The <see cref="DropInfo"/> class holds all of the framework's information about the current
|
||||
/// target of a drag. It is used by <see cref="IDropTarget.DragOver"/> method to determine whether
|
||||
/// the current drop target is valid, and by <see cref="IDropTarget.Drop"/> to perform the drop.
|
||||
/// </remarks>
|
||||
public class DropInfo : IDropInfo
|
||||
{
|
||||
private ItemsControl itemParent = null;
|
||||
private UIElement item = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DropInfo class.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="sender">
|
||||
/// The sender of the drag event.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="e">
|
||||
/// The drag event.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="dragInfo">
|
||||
/// Information about the source of the drag, if the drag came from within the framework.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="eventType">
|
||||
/// The type of the underlying event (tunneled or bubbled).
|
||||
/// </param>
|
||||
public DropInfo(object sender, DragEventArgs e, [CanBeNull] DragInfo dragInfo, EventType eventType)
|
||||
{
|
||||
this.DragInfo = dragInfo;
|
||||
this.KeyStates = e.KeyStates;
|
||||
this.EventType = eventType;
|
||||
var dataFormat = dragInfo?.DataFormat;
|
||||
this.Data = dataFormat != null && e.Data.GetDataPresent(dataFormat.Name) ? e.Data.GetData(dataFormat.Name) : e.Data;
|
||||
|
||||
this.VisualTarget = sender as UIElement;
|
||||
// if there is no drop target, find another
|
||||
if (!this.VisualTarget.IsDropTarget())
|
||||
{
|
||||
// try to find next element
|
||||
var element = this.VisualTarget.TryGetNextAncestorDropTargetElement();
|
||||
if (element != null)
|
||||
{
|
||||
this.VisualTarget = element;
|
||||
}
|
||||
}
|
||||
|
||||
// try find ScrollViewer
|
||||
var dropTargetScrollViewer = DragDrop.GetDropTargetScrollViewer(this.VisualTarget);
|
||||
if (dropTargetScrollViewer != null)
|
||||
{
|
||||
this.TargetScrollViewer = dropTargetScrollViewer;
|
||||
}
|
||||
else if (this.VisualTarget is TabControl)
|
||||
{
|
||||
var tabPanel = this.VisualTarget.GetVisualDescendent<TabPanel>();
|
||||
this.TargetScrollViewer = tabPanel?.GetVisualAncestor<ScrollViewer>();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.TargetScrollViewer = this.VisualTarget?.GetVisualDescendent<ScrollViewer>();
|
||||
}
|
||||
|
||||
this.TargetScrollingMode = this.VisualTarget != null ? DragDrop.GetDropScrollingMode(this.VisualTarget) : ScrollingMode.Both;
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!HitTestUtilities.HitTest4Type<TabPanel>(this.VisualTarget, this.DropPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.VisualTarget is ItemsControl)
|
||||
{
|
||||
var itemsControl = (ItemsControl)this.VisualTarget;
|
||||
//System.Diagnostics.Debug.WriteLine(">>> Name = {0}", itemsControl.Name);
|
||||
// get item under the mouse
|
||||
item = itemsControl.GetItemContainerAt(this.DropPosition);
|
||||
var directlyOverItem = item != null;
|
||||
|
||||
this.TargetGroup = itemsControl.FindGroup(this.DropPosition);
|
||||
this.VisualTargetOrientation = itemsControl.GetItemsPanelOrientation();
|
||||
this.VisualTargetFlowDirection = itemsControl.GetItemsPanelFlowDirection();
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
// ok, no item found, so maybe we can found an item at top, left, right or bottom
|
||||
item = itemsControl.GetItemContainerAt(this.DropPosition, this.VisualTargetOrientation);
|
||||
directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
|
||||
}
|
||||
|
||||
if (item == null && this.TargetGroup != null && this.TargetGroup.IsBottomLevel)
|
||||
{
|
||||
var itemData = this.TargetGroup.Items.FirstOrDefault();
|
||||
if (itemData != null)
|
||||
{
|
||||
item = itemsControl.ItemContainerGenerator.ContainerFromItem(itemData) as UIElement;
|
||||
directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
itemParent = ItemsControl.ItemsControlFromItemContainer(item);
|
||||
this.VisualTargetOrientation = itemParent.GetItemsPanelOrientation();
|
||||
this.VisualTargetFlowDirection = itemParent.GetItemsPanelFlowDirection();
|
||||
|
||||
this.InsertIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
|
||||
this.TargetCollection = itemParent.ItemsSource ?? itemParent.Items;
|
||||
|
||||
var tvItem = item as TreeViewItem;
|
||||
|
||||
if (directlyOverItem || tvItem != null)
|
||||
{
|
||||
this.VisualTargetItem = item;
|
||||
this.TargetItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
|
||||
}
|
||||
|
||||
var expandedTVItem = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded;
|
||||
var itemRenderSize = expandedTVItem ? tvItem.GetHeaderSize() : item.RenderSize;
|
||||
|
||||
if (this.VisualTargetOrientation == Orientation.Vertical)
|
||||
{
|
||||
var currentYPos = e.GetPosition(item).Y;
|
||||
var targetHeight = itemRenderSize.Height;
|
||||
|
||||
var topGap = targetHeight * 0.25;
|
||||
var bottomGap = targetHeight * 0.75;
|
||||
if (currentYPos > targetHeight / 2)
|
||||
{
|
||||
if (expandedTVItem && (currentYPos < topGap || currentYPos > bottomGap))
|
||||
{
|
||||
this.VisualTargetItem = tvItem.ItemContainerGenerator.ContainerFromIndex(0) as UIElement;
|
||||
this.TargetItem = this.VisualTargetItem != null ? tvItem.ItemContainerGenerator.ItemFromContainer(this.VisualTargetItem) : null;
|
||||
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
|
||||
this.InsertIndex = 0;
|
||||
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.InsertIndex++;
|
||||
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
|
||||
}
|
||||
|
||||
if (currentYPos > topGap && currentYPos < bottomGap)
|
||||
{
|
||||
if (tvItem != null)
|
||||
{
|
||||
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
|
||||
this.InsertIndex = this.TargetCollection != null ? this.TargetCollection.OfType<object>().Count() : 0;
|
||||
}
|
||||
this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
|
||||
}
|
||||
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, Y={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentYPos, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentXPos = e.GetPosition(item).X;
|
||||
var targetWidth = itemRenderSize.Width;
|
||||
|
||||
if (this.VisualTargetFlowDirection == FlowDirection.RightToLeft)
|
||||
{
|
||||
if (currentXPos > targetWidth / 2)
|
||||
{
|
||||
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.InsertIndex++;
|
||||
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
|
||||
}
|
||||
}
|
||||
else if (this.VisualTargetFlowDirection == FlowDirection.LeftToRight)
|
||||
{
|
||||
if (currentXPos > targetWidth / 2)
|
||||
{
|
||||
this.InsertIndex++;
|
||||
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75)
|
||||
{
|
||||
if (tvItem != null)
|
||||
{
|
||||
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
|
||||
this.InsertIndex = this.TargetCollection != null ? this.TargetCollection.OfType<object>().Count() : 0;
|
||||
}
|
||||
this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
|
||||
}
|
||||
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, X={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentXPos, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.TargetCollection = itemsControl.ItemsSource ?? itemsControl.Items;
|
||||
this.InsertIndex = itemsControl.Items.Count;
|
||||
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, item=NULL, idx={1}", this.InsertPosition, this.InsertIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.VisualTargetItem = this.VisualTarget;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the drag came from within the framework, this will hold:
|
||||
///
|
||||
/// - The dragged data if a single item was dragged.
|
||||
/// - A typed IEnumerable if multiple items were dragged.
|
||||
/// </remarks>
|
||||
public object Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag,
|
||||
/// if the drag came from within the framework.
|
||||
/// </summary>
|
||||
public IDragInfo DragInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse position relative to the VisualTarget
|
||||
/// </summary>
|
||||
public Point DropPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the class of drop target to display.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
|
||||
/// class.
|
||||
/// </remarks>
|
||||
public Type DropTargetAdorner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed effects for the drop.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order
|
||||
/// for a drop to be possible.
|
||||
/// </remarks>
|
||||
public DragDropEffects Effects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current insert position within <see cref="TargetCollection"/>.
|
||||
/// </summary>
|
||||
public int InsertIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current insert position within the source (unfiltered) <see cref="TargetCollection"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be only used in a Drop action.
|
||||
/// This works only correct with different objects (string, int, etc won't work correct).
|
||||
/// </remarks>
|
||||
public int UnfilteredInsertIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var insertIndex = this.InsertIndex;
|
||||
if (itemParent != null)
|
||||
{
|
||||
var itemSourceAsList = itemParent.ItemsSource.TryGetList();
|
||||
if (itemSourceAsList != null && itemParent.Items != null && itemParent.Items.Count != itemSourceAsList.Count)
|
||||
{
|
||||
if (insertIndex >= 0 && insertIndex < itemParent.Items.Count)
|
||||
{
|
||||
var indexOf = itemSourceAsList.IndexOf(itemParent.Items[insertIndex]);
|
||||
if (indexOf >= 0)
|
||||
{
|
||||
return indexOf;
|
||||
}
|
||||
}
|
||||
else if (itemParent.Items.Count > 0 && insertIndex == itemParent.Items.Count)
|
||||
{
|
||||
var indexOf = itemSourceAsList.IndexOf(itemParent.Items[insertIndex - 1]);
|
||||
if (indexOf >= 0)
|
||||
{
|
||||
return indexOf + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return insertIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection that the target ItemsControl is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
public IEnumerable TargetCollection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that the current drop target is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
public object TargetItem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current group target.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the drag is currently over an ItemsControl with groups, describes the group that
|
||||
/// the drag is currently over.
|
||||
/// </remarks>
|
||||
public CollectionViewGroup TargetGroup { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ScrollViewer control for the visual target.
|
||||
/// </summary>
|
||||
public ScrollViewer TargetScrollViewer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the ScrollingMode for the drop action.
|
||||
/// </summary>
|
||||
public ScrollingMode TargetScrollingMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that is the current drop target.
|
||||
/// </summary>
|
||||
public UIElement VisualTarget { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item in an ItemsControl that is the current drop target.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
public UIElement VisualTargetItem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the orientation of the current drop target.
|
||||
/// </summary>
|
||||
public Orientation VisualTargetOrientation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the orientation of the current drop target.
|
||||
/// </summary>
|
||||
public FlowDirection VisualTargetFlowDirection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the text displayed in the DropDropEffects adorner.
|
||||
/// </summary>
|
||||
public string DestinationText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the effect text displayed in the DropDropEffects adorner.
|
||||
/// </summary>
|
||||
public string EffectText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative position the item will be inserted to compared to the TargetItem
|
||||
/// </summary>
|
||||
public RelativeInsertPosition InsertPosition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag enumeration indicating the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.
|
||||
/// </summary>
|
||||
public DragDropKeyStates KeyStates { get; private set; }
|
||||
|
||||
public bool NotHandled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the target is in the same context as the source, <see cref="DragDrop.DragDropContextProperty" />.
|
||||
/// </summary>
|
||||
public bool IsSameDragDropContextAsSource
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check if DragInfo stuff exists
|
||||
if (this.DragInfo == null || this.DragInfo.VisualSource == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// A target should be exists
|
||||
if (this.VisualTarget == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Source element has a drag context constraint, we need to check the target property matches.
|
||||
var sourceContext = this.DragInfo.VisualSource.GetValue(DragDrop.DragDropContextProperty) as string;
|
||||
if (String.IsNullOrEmpty(sourceContext))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var targetContext = this.VisualTarget.GetValue(DragDrop.DragDropContextProperty) as string;
|
||||
return string.Equals(sourceContext, targetContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current mode of the underlying routed event.
|
||||
/// </summary>
|
||||
public EventType EventType { get; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum RelativeInsertPosition
|
||||
{
|
||||
None = 0,
|
||||
BeforeTargetItem = 1,
|
||||
AfterTargetItem = 2,
|
||||
TargetItemCenter = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public abstract class DropTargetAdorner : Adorner
|
||||
{
|
||||
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
|
||||
public DropTargetAdorner(UIElement adornedElement)
|
||||
: this(adornedElement, (DropInfo)null)
|
||||
{
|
||||
}
|
||||
|
||||
public DropTargetAdorner(UIElement adornedElement, DropInfo dropInfo)
|
||||
: base(adornedElement)
|
||||
{
|
||||
this.DropInfo = dropInfo;
|
||||
this.IsHitTestVisible = false;
|
||||
this.AllowDrop = false;
|
||||
this.SnapsToDevicePixels = true;
|
||||
this.m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
|
||||
this.m_AdornerLayer.Add(this);
|
||||
}
|
||||
|
||||
public DropInfo DropInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets the pen which can be used for the render process.
|
||||
/// </summary>
|
||||
public Pen Pen { get; set; } = new Pen(Brushes.Gray, 2);
|
||||
|
||||
public void Detatch()
|
||||
{
|
||||
this.m_AdornerLayer.Remove(this);
|
||||
}
|
||||
|
||||
internal static DropTargetAdorner Create(Type type, UIElement adornedElement, IDropInfo dropInfo)
|
||||
{
|
||||
if (!typeof(DropTargetAdorner).IsAssignableFrom(type))
|
||||
{
|
||||
throw new InvalidOperationException("The requested adorner class does not derive from DropTargetAdorner.");
|
||||
}
|
||||
return type.GetConstructor(new[] { typeof(UIElement), typeof(DropInfo) })?.Invoke(new object[] { adornedElement, dropInfo }) as DropTargetAdorner;
|
||||
}
|
||||
|
||||
private readonly AdornerLayer m_AdornerLayer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public class DropTargetAdorners
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of the default highlight target adorner.
|
||||
/// </summary>
|
||||
public static Type Highlight { get; } = typeof(DropTargetHighlightAdorner);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the default insert target adorner.
|
||||
/// </summary>
|
||||
public static Type Insert { get; } = typeof(DropTargetInsertionAdorner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public class DropTargetHighlightAdorner : DropTargetAdorner
|
||||
{
|
||||
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
|
||||
public DropTargetHighlightAdorner(UIElement adornedElement)
|
||||
: base(adornedElement, (DropInfo)null)
|
||||
{
|
||||
}
|
||||
|
||||
public DropTargetHighlightAdorner(UIElement adornedElement, DropInfo dropInfo)
|
||||
: base(adornedElement, dropInfo)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, participates in rendering operations that are directed by the layout system.
|
||||
/// The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for
|
||||
/// later asynchronous use by layout and drawing.
|
||||
/// </summary>
|
||||
/// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
var dropInfo = this.DropInfo;
|
||||
var visualTargetItem = dropInfo.VisualTargetItem;
|
||||
if (visualTargetItem != null)
|
||||
{
|
||||
var rect = Rect.Empty;
|
||||
|
||||
var tvItem = visualTargetItem as TreeViewItem;
|
||||
if (tvItem != null && VisualTreeHelper.GetChildrenCount(tvItem) > 0)
|
||||
{
|
||||
var descendant = VisualTreeHelper.GetDescendantBounds(tvItem);
|
||||
var translatePoint = tvItem.TranslatePoint(new Point(), this.AdornedElement);
|
||||
var itemRect = new Rect(translatePoint, tvItem.RenderSize);
|
||||
descendant.Union(itemRect);
|
||||
translatePoint.Offset(1, 0);
|
||||
rect = new Rect(translatePoint, new Size(descendant.Width - translatePoint.X - 1, tvItem.ActualHeight));
|
||||
}
|
||||
if (rect.IsEmpty)
|
||||
{
|
||||
rect = new Rect(visualTargetItem.TranslatePoint(new Point(), this.AdornedElement), VisualTreeHelper.GetDescendantBounds(visualTargetItem).Size);
|
||||
}
|
||||
drawingContext.DrawRoundedRectangle(null, this.Pen, rect, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Controls;
|
||||
using GongSolutions.Wpf.DragDrop.Utilities;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public class DropTargetInsertionAdorner : DropTargetAdorner
|
||||
{
|
||||
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
|
||||
public DropTargetInsertionAdorner(UIElement adornedElement)
|
||||
: base(adornedElement, (DropInfo)null)
|
||||
{
|
||||
}
|
||||
|
||||
public DropTargetInsertionAdorner(UIElement adornedElement, DropInfo dropInfo)
|
||||
: base(adornedElement, dropInfo)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, participates in rendering operations that are directed by the layout system.
|
||||
/// The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for
|
||||
/// later asynchronous use by layout and drawing.
|
||||
/// </summary>
|
||||
/// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
var dropInfo = this.DropInfo;
|
||||
var itemsControl = dropInfo.VisualTarget as ItemsControl;
|
||||
|
||||
if (itemsControl != null)
|
||||
{
|
||||
// Get the position of the item at the insertion index. If the insertion point is
|
||||
// to be after the last item, then get the position of the last item and add an
|
||||
// offset later to draw it at the end of the list.
|
||||
ItemsControl itemParent;
|
||||
|
||||
var visualTargetItem = dropInfo.VisualTargetItem;
|
||||
if (visualTargetItem != null)
|
||||
{
|
||||
itemParent = ItemsControl.ItemsControlFromItemContainer(visualTargetItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemParent = itemsControl;
|
||||
}
|
||||
|
||||
// this could be happen with a thread scenario where items are removed very quickly
|
||||
if (itemParent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemsCount = itemParent.Items.Count;
|
||||
var index = Math.Min(dropInfo.InsertIndex, itemsCount - 1);
|
||||
|
||||
var lastItemInGroup = false;
|
||||
var targetGroup = dropInfo.TargetGroup;
|
||||
if (targetGroup != null && targetGroup.IsBottomLevel && dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.AfterTargetItem))
|
||||
{
|
||||
var indexOf = targetGroup.Items.IndexOf(dropInfo.TargetItem);
|
||||
lastItemInGroup = indexOf == targetGroup.ItemCount - 1;
|
||||
if (lastItemInGroup && dropInfo.InsertIndex != itemsCount)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
var itemContainer = (UIElement)itemParent.ItemContainerGenerator.ContainerFromIndex(index);
|
||||
|
||||
var showAlwaysDropTargetAdorner = itemContainer == null && DragDrop.GetShowAlwaysDropTargetAdorner(itemParent);
|
||||
if (showAlwaysDropTargetAdorner)
|
||||
{
|
||||
itemContainer = itemParent;
|
||||
}
|
||||
|
||||
if (itemContainer != null)
|
||||
{
|
||||
var itemRect = new Rect(itemContainer.TranslatePoint(new Point(), this.AdornedElement), itemContainer.RenderSize);
|
||||
Point point1,
|
||||
point2;
|
||||
double rotation = 0;
|
||||
|
||||
// I really don't know why I did this
|
||||
//
|
||||
// var viewportWidth = double.MaxValue;
|
||||
// var viewportHeight = double.MaxValue;
|
||||
// if (DropInfo.TargetScrollViewer != null)
|
||||
// {
|
||||
// if (DropInfo.TargetScrollViewer.ScrollableWidth != 0)
|
||||
// {
|
||||
// viewportWidth = DropInfo.TargetScrollViewer.ViewportWidth;
|
||||
// }
|
||||
//
|
||||
// if (DropInfo.TargetScrollViewer.ScrollableHeight != 0)
|
||||
// {
|
||||
// viewportHeight = DropInfo.TargetScrollViewer.ViewportHeight;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (dropInfo.VisualTargetOrientation == Orientation.Vertical)
|
||||
{
|
||||
if ((dropInfo.InsertIndex == itemsCount) || lastItemInGroup)
|
||||
{
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
itemRect.Y += itemContainer.RenderSize.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((itemsControl as ListView)?.View is GridView)
|
||||
{
|
||||
var header = itemsControl.GetVisualDescendent<GridViewHeaderRowPresenter>();
|
||||
if (header != null)
|
||||
{
|
||||
itemRect.Y += header.RenderSize.Height;
|
||||
}
|
||||
}
|
||||
else if (itemsControl is DataGrid)
|
||||
{
|
||||
var header = itemsControl.GetVisualDescendent<DataGridColumnHeadersPresenter>();
|
||||
if (header != null)
|
||||
{
|
||||
itemRect.Y += header.RenderSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
itemRect.Y += this.Pen.Thickness;
|
||||
}
|
||||
}
|
||||
|
||||
var itemRectRight = itemRect.Right; //Math.Min(itemRect.Right, viewportWidth);
|
||||
var itemRectLeft = itemRect.X < 0 ? 0 : itemRect.X;
|
||||
point1 = new Point(itemRectLeft, itemRect.Y);
|
||||
point2 = new Point(itemRectRight, itemRect.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dropInfo.VisualTargetFlowDirection == FlowDirection.LeftToRight && dropInfo.InsertIndex == itemsCount)
|
||||
{
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
itemRect.X += itemContainer.RenderSize.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemRect.X += this.Pen.Thickness;
|
||||
}
|
||||
}
|
||||
else if (dropInfo.VisualTargetFlowDirection == FlowDirection.RightToLeft && dropInfo.InsertIndex != itemsCount)
|
||||
{
|
||||
if (itemsCount > 0)
|
||||
{
|
||||
itemRect.X += itemContainer.RenderSize.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemRect.X += this.Pen.Thickness;
|
||||
}
|
||||
}
|
||||
|
||||
var itemRectTop = itemRect.Y < 0 ? 0 : itemRect.Y;
|
||||
var itemRectBottom = itemRect.Bottom; //Math.Min(itemRect.Bottom, viewportHeight);
|
||||
|
||||
point1 = new Point(itemRect.X, itemRectTop);
|
||||
point2 = new Point(itemRect.X, itemRectBottom);
|
||||
rotation = 90;
|
||||
}
|
||||
|
||||
drawingContext.DrawLine(this.Pen, point1, point2);
|
||||
this.DrawTriangle(drawingContext, point1, rotation);
|
||||
this.DrawTriangle(drawingContext, point2, 180 + rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTriangle(DrawingContext drawingContext, Point origin, double rotation)
|
||||
{
|
||||
drawingContext.PushTransform(new TranslateTransform(origin.X, origin.Y));
|
||||
drawingContext.PushTransform(new RotateTransform(rotation));
|
||||
|
||||
drawingContext.DrawGeometry(this.Pen.Brush, null, m_Triangle);
|
||||
|
||||
drawingContext.Pop();
|
||||
drawingContext.Pop();
|
||||
}
|
||||
|
||||
static DropTargetInsertionAdorner()
|
||||
{
|
||||
// Create the pen and triangle in a static constructor and freeze them to improve performance.
|
||||
const int triangleSize = 5;
|
||||
|
||||
var firstLine = new LineSegment(new Point(0, -triangleSize), false);
|
||||
firstLine.Freeze();
|
||||
var secondLine = new LineSegment(new Point(0, triangleSize), false);
|
||||
secondLine.Freeze();
|
||||
|
||||
var figure = new PathFigure { StartPoint = new Point(triangleSize, 0) };
|
||||
figure.Segments.Add(firstLine);
|
||||
figure.Segments.Add(secondLine);
|
||||
figure.Freeze();
|
||||
|
||||
m_Triangle = new PathGeometry();
|
||||
m_Triangle.Figures.Add(figure);
|
||||
m_Triangle.Freeze();
|
||||
}
|
||||
|
||||
private static readonly PathGeometry m_Triangle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public enum EventType
|
||||
{
|
||||
Auto,
|
||||
Tunneled,
|
||||
Bubbled,
|
||||
TunneledBubbled
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<!-- Project properties -->
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net45;net46;netcoreapp3.1</TargetFrameworks>
|
||||
<AssemblyName>GongSolutions.WPF.DragDrop</AssemblyName>
|
||||
<Title>gong-wpf-dragdrop</Title>
|
||||
<RootNamespace>GongSolutions.WPF.DragDrop</RootNamespace>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<!-- reference includes -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WpfAnalyzers" Version="2.4.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="JetBrains.Annotations" Version="2020.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public interface IDragInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the data format which will be used for the drag and drop actions.
|
||||
/// </summary>
|
||||
/// <value>The data format.</value>
|
||||
DataFormat DataFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the drag data.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set by a drag handler in order for a drag to start.
|
||||
/// </remarks>
|
||||
object Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
|
||||
/// </summary>
|
||||
Point DragStartPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point where the cursor was relative to the item being dragged when the drag was started.
|
||||
/// </summary>
|
||||
Point PositionInDraggedItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed effects for the drag.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order
|
||||
/// for a drag to start.
|
||||
/// </remarks>
|
||||
DragDropEffects Effects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse button that initiated the drag.
|
||||
/// </summary>
|
||||
MouseButton MouseButton { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection that the source ItemsControl is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
IEnumerable SourceCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from where the item was dragged.
|
||||
/// </summary>
|
||||
int SourceIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that a dragged item is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
object SourceItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
|
||||
/// </remarks>
|
||||
IEnumerable SourceItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the group from a dragged item if the drag is currently from an ItemsControl with groups.
|
||||
/// </summary>
|
||||
CollectionViewGroup SourceGroup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that initiated the drag.
|
||||
/// </summary>
|
||||
UIElement VisualSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item in an ItemsControl that started the drag.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the control that initiated the drag is an ItemsControl, this property will hold the item
|
||||
/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
|
||||
/// will hold a ListBoxItem.
|
||||
/// </remarks>
|
||||
UIElement VisualSourceItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FlowDirection of the current drag source.
|
||||
/// </summary>
|
||||
FlowDirection VisualSourceFlowDirection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IDataObject"/> which is used by the drag and drop operation. Set it to
|
||||
/// a custom instance if custom drag and drop behavior is needed.
|
||||
/// </summary>
|
||||
object DataObject { get; set; }
|
||||
|
||||
/// <summary>Initiates a drag-and-drop operation.</summary>
|
||||
/// <returns>One of the <see cref="T:System.Windows.DragDropEffects" /> values that specifies the final effect that was performed during the drag-and-drop operation.</returns>
|
||||
Func<DependencyObject, object, DragDropEffects, DragDropEffects> DragDropHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the drag drop copy key state indicating the effect of the drag drop operation.
|
||||
/// </summary>
|
||||
DragDropKeyStates DragDropCopyKeyState { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface implemented by Drag Handlers.
|
||||
/// </summary>
|
||||
public interface IDragSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Queries whether a drag can be started.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dragInfo">
|
||||
/// Information about the drag.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// To allow a drag to be started, the <see cref="DragInfo.Effects"/> property on <paramref name="dragInfo"/>
|
||||
/// should be set to a value other than <see cref="DragDropEffects.None"/>.
|
||||
/// </remarks>
|
||||
void StartDrag(IDragInfo dragInfo);
|
||||
|
||||
/// <summary>
|
||||
/// With this action it's possible to check if the drag/drop operation is allowed to start
|
||||
/// e.g. check for a UIElement inside a list view item, that should not start a drag/drop operation
|
||||
/// </summary>
|
||||
bool CanStartDrag(IDragInfo dragInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drop has occurred.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dropInfo">
|
||||
/// Information about the drop.
|
||||
/// </param>
|
||||
void Dropped(IDropInfo dropInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drag and drop operation has finished.
|
||||
/// </summary>
|
||||
/// <param name="operationResult">The operation result.</param>
|
||||
/// <param name="dragInfo">The drag information.</param>
|
||||
void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the drag handler that a drag has been aborted.
|
||||
/// </summary>
|
||||
void DragCancelled();
|
||||
|
||||
/// <summary>
|
||||
/// Notifies that an exception has occurred upon dragging.
|
||||
/// </summary>
|
||||
/// <param name="exception">
|
||||
/// The exception that occurrred.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Boolean indicating whether the exception is handled in the drag handler.
|
||||
/// False will rethrow the exception.
|
||||
/// </returns>
|
||||
bool TryCatchOccurredException(Exception exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
public interface IDropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the drag data.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the drag came from within the framework, this will hold:
|
||||
///
|
||||
/// - The dragged data if a single item was dragged.
|
||||
/// - A typed IEnumerable if multiple items were dragged.
|
||||
/// </remarks>
|
||||
object Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag,
|
||||
/// if the drag came from within the framework.
|
||||
/// </summary>
|
||||
IDragInfo DragInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mouse position relative to the VisualTarget
|
||||
/// </summary>
|
||||
Point DropPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the class of drop target to display.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
|
||||
/// class.
|
||||
/// </remarks>
|
||||
Type DropTargetAdorner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed effects for the drop.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order
|
||||
/// for a drop to be possible.
|
||||
/// </remarks>
|
||||
DragDropEffects Effects { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current insert position within <see cref="TargetCollection"/>.
|
||||
/// </summary>
|
||||
int InsertIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current insert position within the source (unfiltered) <see cref="TargetCollection"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should be only used in a Drop action.
|
||||
/// This works only correct with different objects (string, int, etc won't work correct).
|
||||
/// </remarks>
|
||||
int UnfilteredInsertIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection that the target ItemsControl is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
IEnumerable TargetCollection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object that the current drop target is bound to.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
object TargetItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current group target.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the drag is currently over an ItemsControl with groups, describes the group that
|
||||
/// the drag is currently over.
|
||||
/// </remarks>
|
||||
CollectionViewGroup TargetGroup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that is the current drop target.
|
||||
/// </summary>
|
||||
UIElement VisualTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item in an ItemsControl that is the current drop target.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// If the current drop target is unbound or not an ItemsControl, this will be null.
|
||||
/// </remarks>
|
||||
UIElement VisualTargetItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the orientation of the current drop target.
|
||||
/// </summary>
|
||||
Orientation VisualTargetOrientation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FlowDirection of the current drop target.
|
||||
/// </summary>
|
||||
FlowDirection VisualTargetFlowDirection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the text displayed in the DropDropEffects adorner.
|
||||
/// </summary>
|
||||
string DestinationText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets and sets the effect text displayed in the DropDropEffects adorner.
|
||||
/// </summary>
|
||||
string EffectText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative position the item will be inserted to compared to the TargetItem
|
||||
/// </summary>
|
||||
RelativeInsertPosition InsertPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag enumeration indicating the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.
|
||||
/// </summary>
|
||||
DragDropKeyStates KeyStates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the drop info should be handled by itself (useful for child elements)
|
||||
/// </summary>
|
||||
bool NotHandled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the target is in the same context as the source, <see cref="DragDrop.DragDropContextProperty" />.
|
||||
/// </summary>
|
||||
bool IsSameDragDropContextAsSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current mode of the underlying routed event.
|
||||
/// </summary>
|
||||
EventType EventType { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface implemented by Drop Handlers.
|
||||
/// </summary>
|
||||
public interface IDropTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the current drag state.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dropInfo">
|
||||
/// Information about the drag.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// To allow a drop at the current drag position, the <see cref="DropInfo.Effects"/> property on
|
||||
/// <paramref name="dropInfo"/> should be set to a value other than <see cref="DragDropEffects.None"/>
|
||||
/// and <see cref="DropInfo.Data"/> should be set to a non-null value.
|
||||
/// </remarks>
|
||||
void DragOver(IDropInfo dropInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a drop.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="dropInfo">
|
||||
/// Information about the drop.
|
||||
/// </param>
|
||||
void Drop(IDropInfo dropInfo);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 431 B |
Binary file not shown.
|
After Width: | Height: | Size: 684 B |
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Icons
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class to provide access to standard application images
|
||||
/// </summary>
|
||||
public static class IconFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the icon which can be used for the effect type None.
|
||||
/// </summary>
|
||||
public static BitmapImage EffectNone { get; } = GetImage("EffectNone.png", 12);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon which can be used for the effect type Copy.
|
||||
/// </summary>
|
||||
public static BitmapImage EffectCopy { get; } = GetImage("EffectCopy.png", 12);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon which can be used for the effect type Move.
|
||||
/// </summary>
|
||||
public static BitmapImage EffectMove { get; } = GetImage("EffectMove.png", 12);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the icon which can be used for the effect type Link.
|
||||
/// </summary>
|
||||
public static BitmapImage EffectLink { get; } = GetImage("EffectLink.png", 12);
|
||||
|
||||
/// <summary>
|
||||
/// Loads an image based on the name and size required.
|
||||
/// Images need to be marked as 'Resource' in the project for them be loaded.
|
||||
/// </summary>
|
||||
/// <param name="iconName">Name of the icon</param>
|
||||
/// <param name="size">The size of the icon</param>
|
||||
/// <returns>The image to be displayed</returns>
|
||||
private static BitmapImage GetImage(string iconName, int size)
|
||||
{
|
||||
var uri = new Uri($@"pack://application:,,,/GongSolutions.Wpf.DragDrop;component/Icons/{iconName}", UriKind.RelativeOrAbsolute);
|
||||
var icon = new BitmapImage(uri);
|
||||
icon.DecodePixelHeight = size;
|
||||
icon.DecodePixelWidth = size;
|
||||
return icon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom cursor.
|
||||
/// </summary>
|
||||
/// <param name="rx"></param>
|
||||
/// <param name="ry"></param>
|
||||
/// <param name="brush"></param>
|
||||
/// <param name="pen"></param>
|
||||
/// <returns></returns>
|
||||
public static Cursor CreateCursor(double rx, double ry, SolidColorBrush brush, Pen pen)
|
||||
{
|
||||
var vis = new DrawingVisual();
|
||||
using (var dc = vis.RenderOpen())
|
||||
{
|
||||
dc.DrawRectangle(brush, new Pen(Brushes.Black, 0.1), new Rect(0, 0, rx, ry));
|
||||
dc.Close();
|
||||
}
|
||||
var rtb = new RenderTargetBitmap(64, 64, 96, 96, PixelFormats.Pbgra32);
|
||||
rtb.Render(vis);
|
||||
|
||||
using (var ms1 = new MemoryStream())
|
||||
{
|
||||
var penc = new PngBitmapEncoder();
|
||||
penc.Frames.Add(BitmapFrame.Create(rtb));
|
||||
penc.Save(ms1);
|
||||
|
||||
var pngBytes = ms1.ToArray();
|
||||
var size = pngBytes.GetLength(0);
|
||||
|
||||
//.cur format spec http://en.wikipedia.org/wiki/ICO_(file_format)
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
{
|
||||
//ICONDIR Structure
|
||||
ms.Write(BitConverter.GetBytes((Int16)0), 0, 2); //Reserved must be zero; 2 bytes
|
||||
ms.Write(BitConverter.GetBytes((Int16)2), 0, 2); //image type 1 = ico 2 = cur; 2 bytes
|
||||
ms.Write(BitConverter.GetBytes((Int16)1), 0, 2); //number of images; 2 bytes
|
||||
}
|
||||
|
||||
{
|
||||
//ICONDIRENTRY structure
|
||||
ms.WriteByte(32); //image width in pixels
|
||||
ms.WriteByte(32); //image height in pixels
|
||||
|
||||
ms.WriteByte(0); //Number of Colors in the color palette. Should be 0 if the image doesn't use a color palette
|
||||
ms.WriteByte(0); //reserved must be 0
|
||||
|
||||
ms.Write(BitConverter.GetBytes((Int16)(rx / 2.0)), 0, 2); //2 bytes. In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
|
||||
ms.Write(BitConverter.GetBytes((Int16)(ry / 2.0)), 0, 2); //2 bytes. In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top.
|
||||
|
||||
ms.Write(BitConverter.GetBytes(size), 0, 4); //Specifies the size of the image's data in bytes
|
||||
ms.Write(BitConverter.GetBytes((Int32)22), 0, 4); //Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
|
||||
}
|
||||
|
||||
ms.Write(pngBytes, 0, size); //write the png data.
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return new Cursor(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Markup;
|
||||
|
||||
[assembly: XmlnsPrefix("urn:gong-wpf-dragdrop", "dd")]
|
||||
[assembly: XmlnsDefinition("urn:gong-wpf-dragdrop", "GongSolutions.Wpf.DragDrop")]
|
||||
[assembly: XmlnsDefinition("urn:gong-wpf-dragdrop", "GongSolutions.Wpf.DragDrop.Utilities")]
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("940084f7-d48e-41b3-9e0d-cf574d587643")]
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace GongSolutions.Wpf.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies how <see cref="T:System.Windows.Controls.ScrollViewer" /> reacts to drop operation.
|
||||
/// </summary>
|
||||
public enum ScrollingMode
|
||||
{
|
||||
None,
|
||||
HorizontalOnly,
|
||||
VerticalOnly,
|
||||
Both
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
/// <summary>
|
||||
/// A helper class for Dpi logicm cause Microsoft hides this with the internal flag.
|
||||
/// </summary>
|
||||
public static class DpiHelper
|
||||
{
|
||||
private static Matrix _transformToDevice;
|
||||
private static Matrix _transformToLogical;
|
||||
|
||||
public static double DpiX = 0d;
|
||||
public static double DpiY = 0d;
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
|
||||
static DpiHelper()
|
||||
{
|
||||
var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
var pixelsPerInchX = (int)dpiXProperty.GetValue(null, null); //SystemParameters.DpiX;
|
||||
DpiX = (double)pixelsPerInchX;
|
||||
var pixelsPerInchY = (int)dpiYProperty.GetValue(null, null); //SystemParameters.Dpi;
|
||||
DpiY = (double)pixelsPerInchY;
|
||||
|
||||
_transformToLogical = Matrix.Identity;
|
||||
_transformToLogical.Scale(96d / (double)pixelsPerInchX, 96d / (double)pixelsPerInchY);
|
||||
_transformToDevice = Matrix.Identity;
|
||||
_transformToDevice.Scale((double)pixelsPerInchX / 96d, (double)pixelsPerInchY / 96d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a point in device independent pixels (1/96") to a point in the system coordinates.
|
||||
/// </summary>
|
||||
/// <param name="logicalPoint">A point in the logical coordinate system.</param>
|
||||
/// <returns>Returns the point converted to the system's coordinates.</returns>
|
||||
public static Point LogicalPixelsToDevice(Point logicalPoint)
|
||||
{
|
||||
return _transformToDevice.Transform(logicalPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a point in system coordinates to a point in device independent pixels (1/96").
|
||||
/// </summary>
|
||||
/// <param name="devicePoint">A point in the physical coordinate system.</param>
|
||||
/// <returns>Returns the point converted to the device independent coordinate system.</returns>
|
||||
public static Point DevicePixelsToLogical(Point devicePoint)
|
||||
{
|
||||
return _transformToLogical.Transform(devicePoint);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public static Rect LogicalRectToDevice(Rect logicalRectangle)
|
||||
{
|
||||
Point topLeft = LogicalPixelsToDevice(new Point(logicalRectangle.Left, logicalRectangle.Top));
|
||||
Point bottomRight = LogicalPixelsToDevice(new Point(logicalRectangle.Right, logicalRectangle.Bottom));
|
||||
|
||||
return new Rect(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
public static Rect DeviceRectToLogical(Rect deviceRectangle)
|
||||
{
|
||||
Point topLeft = DevicePixelsToLogical(new Point(deviceRectangle.Left, deviceRectangle.Top));
|
||||
Point bottomRight = DevicePixelsToLogical(new Point(deviceRectangle.Right, deviceRectangle.Bottom));
|
||||
|
||||
return new Rect(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public static Size LogicalSizeToDevice(Size logicalSize)
|
||||
{
|
||||
Point pt = LogicalPixelsToDevice(new Point(logicalSize.Width, logicalSize.Height));
|
||||
|
||||
return new Size { Width = pt.X, Height = pt.Y };
|
||||
}
|
||||
|
||||
public static Size DeviceSizeToLogical(Size deviceSize)
|
||||
{
|
||||
Point pt = DevicePixelsToLogical(new Point(deviceSize.Width, deviceSize.Height));
|
||||
|
||||
return new Size(pt.X, pt.Y);
|
||||
}
|
||||
|
||||
public static Thickness LogicalThicknessToDevice(Thickness logicalThickness)
|
||||
{
|
||||
Point topLeft = LogicalPixelsToDevice(new Point(logicalThickness.Left, logicalThickness.Top));
|
||||
Point bottomRight = LogicalPixelsToDevice(new Point(logicalThickness.Right, logicalThickness.Bottom));
|
||||
|
||||
return new Thickness(topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
public static class DragDropExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the given element is ignored on drag start (<see cref="DragDrop.DragSourceIgnoreProperty"/>).
|
||||
/// </summary>
|
||||
/// <param name="element">The given element.</param>
|
||||
/// <returns>Element is ignored or not.</returns>
|
||||
public static bool IsDragSourceIgnored(this UIElement element)
|
||||
{
|
||||
return element != null && DragDrop.GetDragSourceIgnore(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given element is ignored on drop action (<see cref="DragDrop.IsDragSourceProperty"/>).
|
||||
/// </summary>
|
||||
/// <param name="element">The given element.</param>
|
||||
/// <returns>Element is ignored or not.</returns>
|
||||
public static bool IsDragSource(this UIElement element)
|
||||
{
|
||||
return element != null && DragDrop.GetIsDragSource(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the given element is ignored on drop action (<see cref="DragDrop.IsDropTargetProperty"/>).
|
||||
/// </summary>
|
||||
/// <param name="element">The given element.</param>
|
||||
/// <returns>Element is ignored or not.</returns>
|
||||
public static bool IsDropTarget(this UIElement element)
|
||||
{
|
||||
return element != null && DragDrop.GetIsDropTarget(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if drop position is directly over element
|
||||
/// </summary>
|
||||
/// <param name="dropPosition">Drop position</param>
|
||||
/// <param name="element">element to check whether or not the drop position is directly over or not</param>
|
||||
/// <param name="relativeToElement">element to which the drop position is related</param>
|
||||
/// <returns>drop position is directly over element or not</returns>
|
||||
public static bool DirectlyOverElement(this Point dropPosition, UIElement element, UIElement relativeToElement)
|
||||
{
|
||||
if (element == null)
|
||||
return false;
|
||||
|
||||
var relativeItemPosition = element.TranslatePoint(new Point(0, 0), relativeToElement);
|
||||
var relativeDropPosition = new Point(dropPosition.X - relativeItemPosition.X, dropPosition.Y - relativeItemPosition.Y);
|
||||
return VisualTreeHelper.GetDescendantBounds(element).Contains(relativeDropPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture screen and create data template containing the captured image
|
||||
/// </summary>
|
||||
/// <param name="element">visual source to capture screen of</param>
|
||||
/// <param name="visualSourceFlowDirection">Flowdirection of visual source</param>
|
||||
/// <returns></returns>
|
||||
public static DataTemplate GetCaptureScreenDataTemplate(this UIElement element, FlowDirection visualSourceFlowDirection)
|
||||
{
|
||||
DataTemplate template = null;
|
||||
var bs = CaptureScreen(element, visualSourceFlowDirection);
|
||||
if (bs != null)
|
||||
{
|
||||
var factory = new FrameworkElementFactory(typeof(Image));
|
||||
factory.SetValue(Image.SourceProperty, bs);
|
||||
factory.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
|
||||
factory.SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality);
|
||||
factory.SetValue(FrameworkElement.WidthProperty, bs.Width);
|
||||
factory.SetValue(FrameworkElement.HeightProperty, bs.Height);
|
||||
factory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Left);
|
||||
factory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Top);
|
||||
template = new DataTemplate { VisualTree = factory };
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
// Helper to generate the image - I grabbed this off Google
|
||||
// somewhere. -- Chris Bordeman cbordeman@gmail.com
|
||||
private static BitmapSource CaptureScreen(Visual target, FlowDirection flowDirection)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dpiX = DpiHelper.DpiX;
|
||||
var dpiY = DpiHelper.DpiY;
|
||||
|
||||
var bounds = VisualTreeHelper.GetDescendantBounds(target);
|
||||
var dpiBounds = DpiHelper.LogicalRectToDevice(bounds);
|
||||
|
||||
var pixelWidth = (int)Math.Ceiling(dpiBounds.Width);
|
||||
var pixelHeight = (int)Math.Ceiling(dpiBounds.Height);
|
||||
if (pixelWidth < 0 || pixelHeight < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rtb = new RenderTargetBitmap(pixelWidth, pixelHeight, dpiX, dpiY, PixelFormats.Pbgra32);
|
||||
|
||||
var dv = new DrawingVisual();
|
||||
using (var ctx = dv.RenderOpen())
|
||||
{
|
||||
var vb = new VisualBrush(target);
|
||||
if (flowDirection == FlowDirection.RightToLeft)
|
||||
{
|
||||
var transformGroup = new TransformGroup();
|
||||
transformGroup.Children.Add(new ScaleTransform(-1, 1));
|
||||
transformGroup.Children.Add(new TranslateTransform(bounds.Size.Width - 1, 0));
|
||||
ctx.PushTransform(transformGroup);
|
||||
}
|
||||
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
|
||||
}
|
||||
|
||||
rtb.Render(dv);
|
||||
|
||||
return rtb;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
public static class HitTestUtilities
|
||||
{
|
||||
public static bool HitTest4Type<T>(object sender, Point elementPosition)
|
||||
where T : UIElement
|
||||
{
|
||||
var uiElement = GetHitTestElement4Type<T>(sender, elementPosition);
|
||||
return uiElement != null && uiElement.Visibility == Visibility.Visible;
|
||||
}
|
||||
|
||||
private static T GetHitTestElement4Type<T>(object sender, Point elementPosition)
|
||||
where T : UIElement
|
||||
{
|
||||
var visual = sender as Visual;
|
||||
if (visual == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var hit = VisualTreeHelper.HitTest(visual, elementPosition);
|
||||
if (hit == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var uiElement = hit.VisualHit.GetVisualAncestor<T>();
|
||||
return uiElement;
|
||||
}
|
||||
|
||||
public static bool HitTest4GridViewColumnHeader(object sender, Point elementPosition)
|
||||
{
|
||||
if (sender is ListView)
|
||||
{
|
||||
// no drag&drop for column header
|
||||
var columnHeader = GetHitTestElement4Type<GridViewColumnHeader>(sender, elementPosition);
|
||||
if (columnHeader != null && (columnHeader.Role == GridViewColumnHeaderRole.Floating || columnHeader.Visibility == Visibility.Visible))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HitTest4DataGridTypes(object sender, Point elementPosition)
|
||||
{
|
||||
if (sender is DataGrid)
|
||||
{
|
||||
// no drag&drop for column header
|
||||
var columnHeader = GetHitTestElement4Type<DataGridColumnHeader>(sender, elementPosition);
|
||||
if (columnHeader != null && columnHeader.Visibility == Visibility.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// no drag&drop for row header
|
||||
var rowHeader = GetHitTestElement4Type<DataGridRowHeader>(sender, elementPosition);
|
||||
if (rowHeader != null && rowHeader.Visibility == Visibility.Visible)
|
||||
{
|
||||
// no drag&drop for row header gripper
|
||||
var thumb = GetHitTestElement4Type<Thumb>(sender, elementPosition);
|
||||
if (thumb != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// drag&drop only for data grid row
|
||||
var dataRow = GetHitTestElement4Type<DataGridRow>(sender, elementPosition);
|
||||
return dataRow == null || Equals(dataRow.DataContext, CollectionView.NewItemPlaceholder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HitTest4DataGridTypesOnDragOver(object sender, Point elementPosition)
|
||||
{
|
||||
if (sender is DataGrid)
|
||||
{
|
||||
// no drag&drop on column header
|
||||
var columnHeader = GetHitTestElement4Type<DataGridColumnHeader>(sender, elementPosition);
|
||||
if (columnHeader != null && columnHeader.Visibility == Visibility.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// thx to @osicka from issue #84
|
||||
///
|
||||
/// e.g. original source is part of a popup (e.g. ComboBox drop down), the hit test needs to be done on the original source.
|
||||
/// Because the popup is not attached to the visual tree of the sender.
|
||||
/// This function test this by looping back from the original source to the sender and if it didn't end up at the sender stopped the drag.
|
||||
/// </summary>
|
||||
public static bool IsNotPartOfSender(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var visual = e.OriginalSource as Visual;
|
||||
if (visual == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var hit = VisualTreeHelper.HitTest(visual, e.GetPosition((IInputElement)visual));
|
||||
|
||||
if (hit == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var depObj = e.OriginalSource as DependencyObject;
|
||||
if (depObj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (depObj == sender)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var item = VisualTreeHelper.GetParent(depObj.FindVisualTreeRoot());
|
||||
//var item = VisualTreeHelper.GetParent(e.OriginalSource as DependencyObject);
|
||||
|
||||
while (item != null && item != sender)
|
||||
{
|
||||
item = VisualTreeHelper.GetParent(item);
|
||||
}
|
||||
return item != sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
public static class ItemsControlExtensions
|
||||
{
|
||||
public static CollectionViewGroup FindGroup(this ItemsControl itemsControl, Point position)
|
||||
{
|
||||
if (itemsControl.Items.Groups == null || itemsControl.Items.Groups.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var element = itemsControl.InputHitTest(position) as DependencyObject;
|
||||
if (element != null)
|
||||
{
|
||||
var groupItem = element.GetVisualAncestor<GroupItem>();
|
||||
|
||||
// drag after last item - get group of it
|
||||
if (groupItem == null && itemsControl.Items.Count > 0)
|
||||
{
|
||||
var lastItem = itemsControl.ItemContainerGenerator.ContainerFromItem(itemsControl.Items.GetItemAt(itemsControl.Items.Count - 1)) as FrameworkElement;
|
||||
if (lastItem != null)
|
||||
{
|
||||
var itemEndpoint = lastItem.PointToScreen(new Point(lastItem.ActualWidth, lastItem.ActualHeight));
|
||||
var positionToScreen = itemsControl.PointToScreen(position);
|
||||
switch (itemsControl.GetItemsPanelOrientation())
|
||||
{
|
||||
case Orientation.Horizontal:
|
||||
// assume LeftToRight
|
||||
groupItem = itemEndpoint.X <= positionToScreen.X ? lastItem.GetVisualAncestor<GroupItem>() : null;
|
||||
break;
|
||||
case Orientation.Vertical:
|
||||
groupItem = itemEndpoint.Y <= positionToScreen.Y ? lastItem.GetVisualAncestor<GroupItem>() : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (groupItem != null)
|
||||
{
|
||||
return groupItem.Content as CollectionViewGroup;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool CanSelectMultipleItems(this ItemsControl itemsControl)
|
||||
{
|
||||
if (itemsControl is MultiSelector multiSelector)
|
||||
{
|
||||
// The CanSelectMultipleItems property is protected. Use reflection to
|
||||
// get its value anyway.
|
||||
var propertyInfo = multiSelector.GetType().GetProperty("CanSelectMultipleItems", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
return propertyInfo != null && (bool)propertyInfo.GetValue(multiSelector, null);
|
||||
}
|
||||
else if (itemsControl is ListBox listBox)
|
||||
{
|
||||
return listBox.SelectionMode != SelectionMode.Single;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static UIElement GetItemContainer(this ItemsControl itemsControl, DependencyObject child)
|
||||
{
|
||||
bool isItemContainer;
|
||||
var itemType = GetItemContainerType(itemsControl, out isItemContainer);
|
||||
|
||||
if (itemType != null)
|
||||
{
|
||||
return isItemContainer
|
||||
? (UIElement)child.GetVisualAncestor(itemType, itemsControl)
|
||||
: (UIElement)child.GetVisualAncestor(itemType, itemsControl, itemsControl.GetType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UIElement GetItemContainerAt(this ItemsControl itemsControl, Point position)
|
||||
{
|
||||
var inputElement = itemsControl.InputHitTest(position);
|
||||
|
||||
var uiElement = inputElement as UIElement;
|
||||
if (uiElement != null)
|
||||
{
|
||||
return GetItemContainer(itemsControl, uiElement);
|
||||
}
|
||||
|
||||
// ContentElement's such as Run's within TextBlock's could not be used as drop target items, because they are not UIElement's.
|
||||
var contentElement = inputElement as ContentElement;
|
||||
if (contentElement != null)
|
||||
{
|
||||
return GetItemContainer(itemsControl, contentElement);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UIElement GetItemContainerAt(this ItemsControl itemsControl, Point position, Orientation searchDirection)
|
||||
{
|
||||
bool isItemContainer;
|
||||
var itemContainerType = GetItemContainerType(itemsControl, out isItemContainer);
|
||||
|
||||
Geometry hitTestGeometry;
|
||||
|
||||
if (typeof(TreeViewItem).IsAssignableFrom(itemContainerType))
|
||||
{
|
||||
hitTestGeometry = new LineGeometry(new Point(0, position.Y), new Point(itemsControl.RenderSize.Width, position.Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
var geometryGroup = new GeometryGroup();
|
||||
geometryGroup.Children.Add(new LineGeometry(new Point(0, position.Y), new Point(itemsControl.RenderSize.Width, position.Y)));
|
||||
geometryGroup.Children.Add(new LineGeometry(new Point(position.X, 0), new Point(position.X, itemsControl.RenderSize.Height)));
|
||||
hitTestGeometry = geometryGroup;
|
||||
}
|
||||
|
||||
var hits = new HashSet<DependencyObject>();
|
||||
|
||||
VisualTreeHelper.HitTest(itemsControl,
|
||||
obj =>
|
||||
{
|
||||
// Viewport3D is not good for us
|
||||
// Stop on ScrollBar to improve performance (e.g. at DataGrid)
|
||||
if (obj is Viewport3D || (itemsControl is DataGrid && obj is ScrollBar))
|
||||
{
|
||||
return HitTestFilterBehavior.Stop;
|
||||
}
|
||||
return HitTestFilterBehavior.Continue;
|
||||
},
|
||||
result =>
|
||||
{
|
||||
var itemContainer = isItemContainer
|
||||
? result.VisualHit.GetVisualAncestor(itemContainerType, itemsControl)
|
||||
: result.VisualHit.GetVisualAncestor(itemContainerType, itemsControl, itemsControl.GetType());
|
||||
if (itemContainer != null && ((UIElement)itemContainer).IsVisible == true)
|
||||
{
|
||||
var tvItem = itemContainer as TreeViewItem;
|
||||
if (tvItem != null)
|
||||
{
|
||||
var tv = tvItem.GetVisualAncestor<TreeView>();
|
||||
if (tv == itemsControl)
|
||||
{
|
||||
hits.Add(itemContainer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemsControl.ItemContainerGenerator.IndexFromContainer(itemContainer) >= 0)
|
||||
{
|
||||
hits.Add(itemContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return HitTestResultBehavior.Continue;
|
||||
},
|
||||
new GeometryHitTestParameters(hitTestGeometry));
|
||||
|
||||
return GetClosest(itemsControl, hits, position, searchDirection);
|
||||
}
|
||||
|
||||
public static Type GetItemContainerType(this ItemsControl itemsControl, out bool isItemContainer)
|
||||
{
|
||||
// determines if the itemsControl is not a ListView, ListBox or TreeView
|
||||
isItemContainer = false;
|
||||
|
||||
if (typeof(TabControl).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return typeof(TabItem);
|
||||
}
|
||||
|
||||
if (typeof(DataGrid).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return typeof(DataGridRow);
|
||||
}
|
||||
|
||||
// There is no safe way to get the item container type for an ItemsControl.
|
||||
// First hard-code the types for the common ItemsControls.
|
||||
//if (itemsControl.GetType().IsAssignableFrom(typeof(ListView)))
|
||||
if (typeof(ListView).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return typeof(ListViewItem);
|
||||
}
|
||||
//if (itemsControl.GetType().IsAssignableFrom(typeof(ListBox)))
|
||||
else if (typeof(ListBox).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return typeof(ListBoxItem);
|
||||
}
|
||||
//else if (itemsControl.GetType().IsAssignableFrom(typeof(TreeView)))
|
||||
else if (typeof(TreeView).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return typeof(TreeViewItem);
|
||||
}
|
||||
|
||||
// Otherwise look for the control's ItemsPresenter, get it's child panel and the first
|
||||
// child of that *should* be an item container.
|
||||
//
|
||||
// If the control currently has no items, we're out of luck.
|
||||
if (itemsControl.Items.Count > 0)
|
||||
{
|
||||
var itemsPresenters = itemsControl.GetVisualDescendents<ItemsPresenter>();
|
||||
|
||||
foreach (var itemsPresenter in itemsPresenters)
|
||||
{
|
||||
if (VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
|
||||
{
|
||||
var panel = VisualTreeHelper.GetChild(itemsPresenter, 0);
|
||||
var itemContainer = VisualTreeHelper.GetChildrenCount(panel) > 0
|
||||
? VisualTreeHelper.GetChild(panel, 0)
|
||||
: null;
|
||||
|
||||
// Ensure that this actually *is* an item container by checking it with
|
||||
// ItemContainerGenerator.
|
||||
if (itemContainer != null &&
|
||||
!(itemContainer is GroupItem) &&
|
||||
itemsControl.ItemContainerGenerator.IndexFromContainer(itemContainer) != -1)
|
||||
{
|
||||
isItemContainer = true;
|
||||
return itemContainer.GetType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Orientation which will be used for the drag drop action.
|
||||
/// Normally it will be look up to find the correct orientaion of the inner ItemsPanel,
|
||||
/// but sometimes it's necessary to force the oreintation, if the look up is wrong.
|
||||
/// If so, the ItemsPanelOrientation value is taken.
|
||||
/// </summary>
|
||||
/// <param name="itemsControl">The ItemsControl for the look up.</param>
|
||||
/// <returns>Orientation for the given ItemsControl.</returns>
|
||||
public static Orientation GetItemsPanelOrientation(this ItemsControl itemsControl)
|
||||
{
|
||||
var itemsPanelOrientation = DragDrop.GetItemsPanelOrientation(itemsControl);
|
||||
if (itemsPanelOrientation.HasValue)
|
||||
{
|
||||
return itemsPanelOrientation.Value;
|
||||
}
|
||||
|
||||
if (itemsControl is TabControl)
|
||||
{
|
||||
//HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition)
|
||||
//var tabPanel = itemsControl.GetVisualDescendent<TabPanel>();
|
||||
var tabControl = (TabControl)itemsControl;
|
||||
return tabControl.TabStripPlacement == Dock.Left || tabControl.TabStripPlacement == Dock.Right ? Orientation.Vertical : Orientation.Horizontal;
|
||||
}
|
||||
|
||||
var itemsPresenter = itemsControl.GetVisualDescendent<ItemsPresenter>() ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement;
|
||||
if (itemsPresenter != null && VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
|
||||
{
|
||||
var itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0);
|
||||
var orientationProperty = itemsPanel.GetType().GetProperty("Orientation", typeof(Orientation));
|
||||
if (orientationProperty != null)
|
||||
{
|
||||
return (Orientation)orientationProperty.GetValue(itemsPanel, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Make a guess!
|
||||
return Orientation.Vertical;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FlowDirection which will be used for the drag drop action.
|
||||
/// </summary>
|
||||
/// <param name="itemsControl">The ItemsControl for the look up.</param>
|
||||
/// <returns>FlowDirection for the given ItemsControl.</returns>
|
||||
public static FlowDirection GetItemsPanelFlowDirection(this ItemsControl itemsControl)
|
||||
{
|
||||
var itemsPresenter = itemsControl.GetVisualDescendent<ItemsPresenter>() ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement;
|
||||
if (itemsPresenter != null && VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
|
||||
{
|
||||
var itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0);
|
||||
var flowDirectionProperty = itemsPanel.GetType().GetProperty("FlowDirection", typeof(FlowDirection));
|
||||
if (flowDirectionProperty != null)
|
||||
{
|
||||
return (FlowDirection)flowDirectionProperty.GetValue(itemsPanel, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Make a guess!
|
||||
return FlowDirection.LeftToRight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given object as selected item at the ItemsControl.
|
||||
/// </summary>
|
||||
/// <param name="itemsControl">The ItemsControl which contains the item.</param>
|
||||
/// <param name="item">The object which should be selected.</param>
|
||||
public static void SetSelectedItem(this ItemsControl itemsControl, object item)
|
||||
{
|
||||
if (itemsControl is MultiSelector)
|
||||
{
|
||||
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
var selectionMode = ((ListBox)itemsControl).SelectionMode;
|
||||
try
|
||||
{
|
||||
// change SelectionMode for UpdateAnchorAndActionItem
|
||||
((ListBox)itemsControl).SetCurrentValue(ListBox.SelectionModeProperty, SelectionMode.Single);
|
||||
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
((ListBox)itemsControl).SetCurrentValue(ListBox.SelectionModeProperty, selectionMode);
|
||||
}
|
||||
}
|
||||
else if (itemsControl is TreeViewItem)
|
||||
{
|
||||
// clear old selected item
|
||||
var treeView = ItemsControl.ItemsControlFromItemContainer((TreeViewItem)itemsControl);
|
||||
if (treeView != null)
|
||||
{
|
||||
var prevSelectedItem = treeView.GetValue(TreeView.SelectedItemProperty);
|
||||
if (prevSelectedItem != null)
|
||||
{
|
||||
var prevSelectedTreeViewItem = treeView.ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
|
||||
if (prevSelectedTreeViewItem != null)
|
||||
{
|
||||
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// set new selected item
|
||||
// TreeView.SelectedItemProperty is a read only property, so we must set the selection on the TreeViewItem itself
|
||||
var treeViewItem = ((TreeViewItem)itemsControl).ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
|
||||
if (treeViewItem != null)
|
||||
{
|
||||
treeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, true);
|
||||
}
|
||||
}
|
||||
else if (itemsControl is TreeView)
|
||||
{
|
||||
// clear old selected item
|
||||
var prevSelectedItem = ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
|
||||
if (prevSelectedItem != null)
|
||||
{
|
||||
var prevSelectedTreeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
|
||||
if (prevSelectedTreeViewItem != null)
|
||||
{
|
||||
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
|
||||
}
|
||||
}
|
||||
// set new selected item
|
||||
// TreeView.SelectedItemProperty is a read only property, so we must set the selection on the TreeViewItem itself
|
||||
var treeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
|
||||
if (treeViewItem != null)
|
||||
{
|
||||
treeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, true);
|
||||
}
|
||||
}
|
||||
else if (itemsControl is Selector)
|
||||
{
|
||||
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the selected items.
|
||||
/// </summary>
|
||||
/// <param name="itemsControl">The items control.</param>
|
||||
public static void ClearSelectedItems(this ItemsControl itemsControl)
|
||||
{
|
||||
if (itemsControl is MultiSelector)
|
||||
{
|
||||
if (((MultiSelector)itemsControl).CanSelectMultipleItems())
|
||||
{
|
||||
((MultiSelector)itemsControl).SelectedItems.Clear();
|
||||
}
|
||||
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
if (((ListBox)itemsControl).CanSelectMultipleItems())
|
||||
{
|
||||
((ListBox)itemsControl).SelectedItems.Clear();
|
||||
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
}
|
||||
}
|
||||
else if (itemsControl is TreeViewItem)
|
||||
{
|
||||
var treeView = ItemsControl.ItemsControlFromItemContainer((TreeViewItem)itemsControl);
|
||||
treeView?.ClearSelectedItems();
|
||||
}
|
||||
else if (itemsControl is TreeView)
|
||||
{
|
||||
// clear old selected item
|
||||
var prevSelectedItem = ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
|
||||
if (prevSelectedItem != null)
|
||||
{
|
||||
var prevSelectedTreeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
|
||||
if (prevSelectedTreeViewItem != null)
|
||||
{
|
||||
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (itemsControl is Selector)
|
||||
{
|
||||
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static object GetSelectedItem(this ItemsControl itemsControl)
|
||||
{
|
||||
if (itemsControl is MultiSelector)
|
||||
{
|
||||
return ((MultiSelector)itemsControl).SelectedItem;
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
return ((ListBox)itemsControl).SelectedItem;
|
||||
}
|
||||
else if (itemsControl is TreeView)
|
||||
{
|
||||
return ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
|
||||
}
|
||||
else if (itemsControl is Selector)
|
||||
{
|
||||
return ((Selector)itemsControl).SelectedItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable GetSelectedItems(this ItemsControl itemsControl)
|
||||
{
|
||||
//if (itemsControl.GetType().IsAssignableFrom(typeof(MultiSelector)))
|
||||
if (typeof(MultiSelector).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return ((MultiSelector)itemsControl).SelectedItems;
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
var listBox = (ListBox)itemsControl;
|
||||
|
||||
if (listBox.SelectionMode == SelectionMode.Single)
|
||||
{
|
||||
return Enumerable.Repeat(listBox.SelectedItem, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return listBox.SelectedItems;
|
||||
}
|
||||
}
|
||||
//else if (itemsControl.GetType().IsAssignableFrom(typeof(TreeView)))
|
||||
else if (typeof(TreeView).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return Enumerable.Repeat(((TreeView)itemsControl).SelectedItem, 1);
|
||||
}
|
||||
//else if (itemsControl.GetType().IsAssignableFrom(typeof(Selector)))
|
||||
else if (typeof(Selector).IsAssignableFrom(itemsControl.GetType()))
|
||||
{
|
||||
return Enumerable.Repeat(((Selector)itemsControl).SelectedItem, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetItemSelected(this ItemsControl itemsControl, object item)
|
||||
{
|
||||
if (itemsControl is MultiSelector)
|
||||
{
|
||||
return ((MultiSelector)itemsControl).SelectedItems.Contains(item);
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
return ((ListBox)itemsControl).SelectedItems.Contains(item);
|
||||
}
|
||||
else if (itemsControl is TreeView)
|
||||
{
|
||||
return ((TreeView)itemsControl).SelectedItem == item;
|
||||
}
|
||||
else if (itemsControl is Selector)
|
||||
{
|
||||
return ((Selector)itemsControl).SelectedItem == item;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetItemSelected(this ItemsControl itemsControl, object item, bool itemSelected)
|
||||
{
|
||||
if (itemsControl is MultiSelector)
|
||||
{
|
||||
var multiSelector = (MultiSelector)itemsControl;
|
||||
|
||||
if (multiSelector.CanSelectMultipleItems())
|
||||
{
|
||||
if (itemSelected)
|
||||
{
|
||||
multiSelector.SelectedItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
multiSelector.SelectedItems.Remove(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
multiSelector.SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
if (itemSelected)
|
||||
{
|
||||
multiSelector.SetCurrentValue(Selector.SelectedItemProperty, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (itemsControl is ListBox)
|
||||
{
|
||||
var listBox = (ListBox)itemsControl;
|
||||
|
||||
if (listBox.SelectionMode != SelectionMode.Single)
|
||||
{
|
||||
if (itemSelected)
|
||||
{
|
||||
listBox.SelectedItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
listBox.SelectedItems.Remove(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
listBox.SetCurrentValue(Selector.SelectedItemProperty, null);
|
||||
if (itemSelected)
|
||||
{
|
||||
listBox.SetCurrentValue(Selector.SelectedItemProperty, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemSelected)
|
||||
{
|
||||
itemsControl.SetSelectedItem(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemsControl.SetSelectedItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static UIElement GetClosest(ItemsControl itemsControl, IEnumerable<DependencyObject> items,
|
||||
Point position, Orientation searchDirection)
|
||||
{
|
||||
//Console.WriteLine("GetClosest - {0}", itemsControl.ToString());
|
||||
|
||||
UIElement closest = null;
|
||||
var closestDistance = double.MaxValue;
|
||||
|
||||
foreach (var i in items)
|
||||
{
|
||||
var uiElement = i as UIElement;
|
||||
|
||||
if (uiElement != null)
|
||||
{
|
||||
var p = uiElement.TransformToAncestor(itemsControl).Transform(new Point(0, 0));
|
||||
var distance = double.MaxValue;
|
||||
|
||||
if (itemsControl is TreeView)
|
||||
{
|
||||
var xDiff = position.X - p.X;
|
||||
var yDiff = position.Y - p.Y;
|
||||
var hyp = Math.Sqrt(Math.Pow(xDiff, 2d) + Math.Pow(yDiff, 2d));
|
||||
distance = Math.Abs(hyp);
|
||||
}
|
||||
else
|
||||
{
|
||||
var itemParent = ItemsControl.ItemsControlFromItemContainer(uiElement);
|
||||
if (itemParent != null && itemParent != itemsControl)
|
||||
{
|
||||
searchDirection = itemParent.GetItemsPanelOrientation();
|
||||
}
|
||||
switch (searchDirection)
|
||||
{
|
||||
case Orientation.Horizontal:
|
||||
distance = position.X <= p.X ? p.X - position.X : position.X - uiElement.RenderSize.Width - p.X;
|
||||
break;
|
||||
case Orientation.Vertical:
|
||||
distance = position.Y <= p.Y ? p.Y - position.Y : position.Y - uiElement.RenderSize.Height - p.Y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closest = uiElement;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
public static class RootElementFinder
|
||||
{
|
||||
public static UIElement FindRoot(DependencyObject visual)
|
||||
{
|
||||
var parentWindow = Window.GetWindow(visual);
|
||||
var rootElement = parentWindow != null ? parentWindow.Content as UIElement : null;
|
||||
if (rootElement == null)
|
||||
{
|
||||
if (Application.Current != null && Application.Current.MainWindow != null)
|
||||
{
|
||||
rootElement = Application.Current.MainWindow.Content as UIElement;
|
||||
}
|
||||
if (rootElement == null)
|
||||
{
|
||||
rootElement = visual.GetVisualAncestor<Page>() ?? visual.GetVisualAncestor<UserControl>() as UIElement;
|
||||
}
|
||||
}
|
||||
// i don't want the fu... windows forms reference
|
||||
// if (rootElement == null) {
|
||||
// var elementHost = m_DragInfo.VisualSource.GetVisualAncestor<ElementHost>();
|
||||
// rootElement = elementHost != null ? elementHost.Child : null;
|
||||
// }
|
||||
return rootElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for TreeViewItem
|
||||
/// </summary>
|
||||
public static class TreeViewItemExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Try get the height of the header part for the given TreeViewItem.
|
||||
/// If there is no PART_Header it will return Size.Empty.
|
||||
/// </summary>
|
||||
/// <param name="item">The TreeViewItem.</param>
|
||||
public static Size GetHeaderSize(this TreeViewItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return Size.Empty;
|
||||
}
|
||||
var header = GetHeaderControl(item);
|
||||
return header != null ? new Size(header.ActualWidth, header.ActualHeight) : item.RenderSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try get the header part of the given TreeViewItem.
|
||||
/// If there is no PART_Header it will return null.
|
||||
/// </summary>
|
||||
/// <param name="item">The TreeViewItem.</param>
|
||||
public static FrameworkElement GetHeaderControl(this TreeViewItem item)
|
||||
{
|
||||
return item?.Template?.FindName("PART_Header", item) as FrameworkElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
public static class TypeUtilities
|
||||
{
|
||||
public static IEnumerable CreateDynamicallyTypedList(IEnumerable source)
|
||||
{
|
||||
var type = GetCommonBaseClass(source);
|
||||
var listType = typeof(List<>).MakeGenericType(type);
|
||||
var addMethod = listType.GetMethod("Add");
|
||||
var list = listType.GetConstructor(Type.EmptyTypes).Invoke(null);
|
||||
|
||||
foreach (var o in source)
|
||||
{
|
||||
addMethod.Invoke(list, new[] { o });
|
||||
}
|
||||
|
||||
return (IEnumerable)list;
|
||||
}
|
||||
|
||||
public static Type GetCommonBaseClass(IEnumerable e)
|
||||
{
|
||||
var types = e.Cast<object>().Select(o => o.GetType()).ToArray<Type>();
|
||||
return GetCommonBaseClass(types);
|
||||
}
|
||||
|
||||
public static Type GetCommonBaseClass(Type[] types)
|
||||
{
|
||||
if (types.Length == 0)
|
||||
{
|
||||
return typeof(object);
|
||||
}
|
||||
|
||||
var ret = types[0];
|
||||
|
||||
for (var i = 1; i < types.Length; ++i)
|
||||
{
|
||||
if (types[i].IsAssignableFrom(ret))
|
||||
{
|
||||
ret = types[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will always terminate when ret == typeof(object)
|
||||
while (!ret.IsAssignableFrom(types[i]))
|
||||
{
|
||||
ret = ret.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enumerable as list.
|
||||
/// If enumerable is an ICollectionView then it returns the SourceCollection as list.
|
||||
/// </summary>
|
||||
/// <param name="enumerable">The enumerable.</param>
|
||||
/// <returns>Returns a list.</returns>
|
||||
public static IList TryGetList(this IEnumerable enumerable)
|
||||
{
|
||||
if (enumerable is ICollectionView)
|
||||
{
|
||||
return ((ICollectionView)enumerable).SourceCollection as IList;
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = enumerable as IList;
|
||||
return list ?? (enumerable != null ? enumerable.OfType<object>().ToList() : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media.Media3D;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace GongSolutions.Wpf.DragDrop.Utilities
|
||||
{
|
||||
public static class VisualTreeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the next ancestor element which is a drop target.
|
||||
/// </summary>
|
||||
/// <param name="element">The start element.</param>
|
||||
/// <returns>The first element which is a drop target.</returns>
|
||||
public static UIElement TryGetNextAncestorDropTargetElement(this UIElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var ancestor = element.GetVisualAncestor<UIElement>();
|
||||
while (ancestor != null)
|
||||
{
|
||||
if (ancestor.IsDropTarget())
|
||||
{
|
||||
return ancestor;
|
||||
}
|
||||
ancestor = ancestor.GetVisualAncestor<UIElement>();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static DependencyObject FindVisualTreeRoot(this DependencyObject d)
|
||||
{
|
||||
var current = d;
|
||||
var result = d;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
result = current;
|
||||
if (current is Visual || current is Visual3D)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're in Logical Land then we must walk
|
||||
// up the logical tree until we find a
|
||||
// Visual/Visual3D to get us back to Visual Land.
|
||||
current = LogicalTreeHelper.GetParent(current);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static T GetVisualAncestor<T>(this DependencyObject d)
|
||||
where T : class
|
||||
{
|
||||
var item = VisualTreeHelper.GetParent(d.FindVisualTreeRoot());
|
||||
|
||||
while (item != null)
|
||||
{
|
||||
var itemAsT = item as T;
|
||||
if (itemAsT != null)
|
||||
{
|
||||
return itemAsT;
|
||||
}
|
||||
item = VisualTreeHelper.GetParent(item);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// find the visual ancestor item by type
|
||||
/// </summary>
|
||||
public static DependencyObject GetVisualAncestor(this DependencyObject d, Type itemSearchType, [NotNull] ItemsControl itemsControl, [NotNull] Type itemContainerSearchType)
|
||||
{
|
||||
if (itemsControl == null) throw new ArgumentNullException(nameof(itemsControl));
|
||||
if (itemContainerSearchType == null) throw new ArgumentNullException(nameof(itemContainerSearchType));
|
||||
|
||||
var visualTreeRoot = d.FindVisualTreeRoot();
|
||||
var currentVisual = VisualTreeHelper.GetParent(visualTreeRoot);
|
||||
|
||||
while (currentVisual != null && itemSearchType != null)
|
||||
{
|
||||
var currentVisualType = currentVisual.GetType();
|
||||
if (currentVisualType == itemSearchType || currentVisualType.IsSubclassOf(itemSearchType))
|
||||
{
|
||||
if (currentVisual is TreeViewItem || itemsControl.ItemContainerGenerator.IndexFromContainer(currentVisual) != -1)
|
||||
{
|
||||
return currentVisual;
|
||||
}
|
||||
}
|
||||
if (itemContainerSearchType.IsAssignableFrom(currentVisualType))
|
||||
{
|
||||
// ok, we found an ItemsControl (maybe an empty)
|
||||
return null;
|
||||
}
|
||||
currentVisual = VisualTreeHelper.GetParent(currentVisual);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// find the visual ancestor by type and go through the visual tree until the given itemsControl will be found
|
||||
/// </summary>
|
||||
public static DependencyObject GetVisualAncestor(this DependencyObject d, Type itemSearchType, [NotNull] ItemsControl itemsControl)
|
||||
{
|
||||
if (itemsControl == null) throw new ArgumentNullException(nameof(itemsControl));
|
||||
|
||||
var visualTreeRoot = d.FindVisualTreeRoot();
|
||||
var currentVisual = VisualTreeHelper.GetParent(visualTreeRoot);
|
||||
DependencyObject lastFoundItemByType = null;
|
||||
|
||||
while (currentVisual != null && itemSearchType != null)
|
||||
{
|
||||
if (currentVisual == itemsControl)
|
||||
{
|
||||
return lastFoundItemByType;
|
||||
}
|
||||
var currentVisualType = currentVisual.GetType();
|
||||
if ((currentVisualType == itemSearchType || currentVisualType.IsSubclassOf(itemSearchType))
|
||||
&& (itemsControl.ItemContainerGenerator.IndexFromContainer(currentVisual) != -1))
|
||||
{
|
||||
lastFoundItemByType = currentVisual;
|
||||
}
|
||||
currentVisual = VisualTreeHelper.GetParent(currentVisual);
|
||||
}
|
||||
|
||||
return lastFoundItemByType;
|
||||
}
|
||||
|
||||
public static T GetVisualDescendent<T>(this DependencyObject d)
|
||||
where T : DependencyObject
|
||||
{
|
||||
return d.GetVisualDescendents<T>().FirstOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetVisualDescendents<T>(this DependencyObject d)
|
||||
where T : DependencyObject
|
||||
{
|
||||
var childCount = VisualTreeHelper.GetChildrenCount(d);
|
||||
|
||||
for (var n = 0; n < childCount; n++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(d, n);
|
||||
|
||||
if (child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
|
||||
foreach (var match in GetVisualDescendents<T>(child))
|
||||
{
|
||||
yield return match;
|
||||
}
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user