项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

View File

@@ -0,0 +1,38 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Project properties -->
<PropertyGroup>
<TargetFrameworks>net45;net46;netcoreapp3.0</TargetFrameworks>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<LangVersion>latest</LangVersion>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoError>$(NoError);CS1591</NoError>
<UseWPF>true</UseWPF>
</PropertyGroup>
<!-- Add the references for all projects and targets -->
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.*" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Remove="**\*.png;**\*.jpg;**\*.ico" />
<Resource Include="**\*.png;**\*.jpg;**\*.ico" />
</ItemGroup>
<!-- SourceLink -->
<PropertyGroup>
<!-- Optional: Declare that the Repository URL can be published to NuSpec -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager to the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Optional: Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<!-- https://github.com/dotnet/sourcelink/blob/master/docs/README.md#embedallsources -->
<!-- <EmbedAllSources>true</EmbedAllSources>-->
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,21 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- AssemblyInfo -->
<PropertyGroup>
<Company>GongSolutions.WPF.DragDrop</Company>
<Product>GongSolutions.WPF.DragDrop</Product>
<Copyright>Copyright © 2013 - $([System.DateTime]::Today.ToString(yyyy)) GongSolutions.WPF.DragDrop</Copyright>
<Description>An easy to use drag'n'drop framework for WPF applications.</Description>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Version>2.0.0.0</Version>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<InformationalVersion>2.0.0.0</InformationalVersion>
</PropertyGroup>
<ItemGroup>
<Page Update="@(Page)" SubType="Designer" Generator="MSBuild:Compile" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28729.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GongSolutions.WPF.DragDrop", "GongSolutions.WPF.DragDrop\GongSolutions.WPF.DragDrop.csproj", "{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Showcase.WPF.DragDrop", "Showcase\Showcase.WPF.DragDrop.csproj", "{4E15F482-1FE9-4614-98F7-31256B643462}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Release|Any CPU.Build.0 = Release|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4DA11A5C-612C-4901-AA97-FF5A87451FCA}
EndGlobalSection
EndGlobal

File diff suppressed because one or more lines are too long

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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; }
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,10 @@
namespace GongSolutions.Wpf.DragDrop
{
public enum EventType
{
Auto,
Tunneled,
Bubbled,
TunneledBubbled
}
}

View File

@@ -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>

View File

@@ -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; }
}
}

View File

@@ -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);
}
}

View File

@@ -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; }
}
}

View File

@@ -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

View File

@@ -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);
}
}
}
}
}

View File

@@ -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")]

View File

@@ -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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
</configuration>

View File

@@ -0,0 +1,253 @@
<Application x:Class="Showcase.WPF.DragDrop.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="TabItemPanelBackgroundBrush" Color="Silver" />
<SolidColorBrush x:Key="TabItemBackgroundIsSelectedBrush" Color="Gray" />
<SolidColorBrush x:Key="TabItemBackgroundIsMouseOverBrush" Color="#A5A5A5" />
<SolidColorBrush x:Key="TabItemForegroundIsSelectedBrush" Color="White" />
<SolidColorBrush x:Key="TabItemSelectorBrush" Color="LawnGreen" />
<Style x:Key="GitHubIssueButtonStyle"
x:Shared="False"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Command" Value="{Binding OpenIssueCommand}" />
<Setter Property="Content">
<Setter.Value>
<iconPacks:PackIconMaterial Width="24"
Height="24"
Kind="GithubCircle" />
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Margin" Value="0 1 4 1" />
<Setter Property="Padding" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid Background="Transparent">
<Ellipse Fill="{TemplateBinding Background}" StrokeThickness="0" />
<ContentPresenter x:Name="PART_ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="#4078C0" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="#315E95" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style x:Key="GitHubPullRequestButtonStyle"
x:Shared="False"
BasedOn="{StaticResource GitHubIssueButtonStyle}"
TargetType="{x:Type Button}">
<Setter Property="Command" Value="{Binding OpenPullRequestCommand}" />
</Style>
<Style x:Key="OpenLinkButtonStyle"
x:Shared="False"
BasedOn="{StaticResource GitHubIssueButtonStyle}"
TargetType="{x:Type Button}">
<Setter Property="Command" Value="{Binding OpenLinkCommand}" />
<Setter Property="Content">
<Setter.Value>
<iconPacks:PackIconFeatherIcons Width="20"
Height="20"
Kind="ExternalLink" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DefaultTabItemStyle" TargetType="{x:Type TabItem}" />
<Style x:Key="DefaultTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Height" Value="100" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTabItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="TabStripPlacement" Value="Top" />
</Style>
<Style x:Key="ShowcaseTabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="PART_Grid"
Margin="0"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<TextBlock Name="PART_Text"
Margin="0"
Padding="10"
VerticalAlignment="Center"
FontSize="14"
Text="{TemplateBinding Header}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
<Rectangle x:Name="PART_Selector"
Width="4"
HorizontalAlignment="Left"
Fill="{StaticResource TabItemSelectorBrush}"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource TabItemBackgroundIsSelectedBrush}" />
<Setter TargetName="PART_Text" Property="Foreground" Value="{StaticResource TabItemForegroundIsSelectedBrush}" />
<Setter TargetName="PART_Selector" Property="Visibility" Value="Visible" />
</Trigger.Setters>
</Trigger>
<Trigger SourceName="PART_Grid" Property="IsMouseOver" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource TabItemBackgroundIsMouseOverBrush}" />
</Trigger.Setters>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="120" />
</Style>
<Style x:Key="ShowcaseTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ItemContainerStyle" Value="{StaticResource ShowcaseTabItemStyle}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<DockPanel LastChildFill="True">
<WrapPanel x:Name="HeaderPanel"
Panel.ZIndex="1"
Background="{StaticResource TabItemPanelBackgroundBrush}"
DockPanel.Dock="Left"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Orientation="Vertical" />
<Border x:Name="ContentPanel"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<ContentPresenter x:Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Top">
<Setter TargetName="HeaderPanel" Property="DockPanel.Dock" Value="Top" />
<Setter TargetName="HeaderPanel" Property="Orientation" Value="Horizontal" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DefaultTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="14" />
<Setter Property="Margin" Value="4" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style x:Key="SampleHeaderTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Gainsboro" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="Gray" />
<Setter Property="Padding" Value="10" />
</Style>
<Style x:Key="DefaultListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultListBoxItemStyle}" TargetType="{x:Type ListBoxItem}" />
<Style x:Key="DefaultListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultListBoxItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
</Style>
<Style x:Key="DefaultItemsControl" TargetType="{x:Type ItemsControl}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="#FF888888" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Height" Value="300" />
<Setter Property="Margin" Value="4" />
</Style>
<Style x:Key="DefaultListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="{x:Type ListViewItem}" />
<Style x:Key="DefaultListViewStyle" TargetType="{x:Type ListView}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultListViewItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
</Style>
<Style x:Key="DefaultTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultTreeViewItemStyle}" TargetType="{x:Type TreeViewItem}" />
<Style x:Key="DefaultTreeViewStyle" TargetType="{x:Type TreeView}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTreeViewItemStyle}" />
<Setter Property="Margin" Value="4" />
</Style>
<Style x:Key="DefaultDataGridStyle" TargetType="{x:Type DataGrid}">
<Setter Property="Height" Value="300" />
<Setter Property="HorizontalGridLinesBrush" Value="Silver" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
<Setter Property="VerticalGridLinesBrush" Value="Silver" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,11 @@
using System.Windows;
namespace Showcase.WPF.DragDrop
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.CustomControls
{
public class CustomListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CustomListBoxItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is CustomListBoxItem;
}
}
public class CustomListBoxItem : ListBoxItem
{
public override string ToString()
{
return base.ToString();
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura DisableCompression="false" />
</Weavers>

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -0,0 +1,43 @@
<Window x:Class="Showcase.WPF.DragDrop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
xmlns:views="clr-namespace:Showcase.WPF.DragDrop.Views"
Title="GongSolutions.WPF.DragDrop"
Width="900"
Height="600"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<TabControl x:Name="MainTabControl" Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="ListBox">
<views:ListBoxSamples />
</TabItem>
<TabItem Header="ListView">
<views:ListViewSamples />
</TabItem>
<TabItem Header="TreeView">
<views:TreeViewSamples />
</TabItem>
<TabItem Header="DataGrid">
<views:DataGridSamples />
</TabItem>
<TabItem Header="TabControl">
<views:TabControlSamples />
</TabItem>
<TabItem Header="Mixed">
<views:MixedSamples />
</TabItem>
<TabItem Header="Fixed #Issues">
<views:Issues />
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using Showcase.WPF.DragDrop.ViewModels;
namespace Showcase.WPF.DragDrop
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
this.Loaded += MainWindowLoaded;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var appArgs = Environment.GetCommandLineArgs();
if (appArgs.Length > 1 && appArgs[1] == "anotherOne")
{
this.MainTabControl.SelectedItem = MainTabControl.Items.OfType<TabItem>().FirstOrDefault(t => (string)t.Header == "Mixed");
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
namespace Showcase.WPF.DragDrop.Models
{
public class ClonableItemModel : ItemModel, ICloneable
{
public ClonableItemModel()
{
}
public ClonableItemModel(int itemIndex)
: base(itemIndex)
{
}
public object Clone()
{
var clonableItemModel = new ClonableItemModel();
clonableItemModel.BindableDoubleValue = this.BindableDoubleValue;
clonableItemModel.SubItemCollection.Clear();
foreach (var subItem in this.SubItemCollection)
{
clonableItemModel.SubItemCollection.Add(subItem);
}
clonableItemModel.SelectedSubItem = this.SelectedSubItem;
clonableItemModel.Index = this.Index;
clonableItemModel.Caption = $"Cloned Item {this.Index}";
return clonableItemModel;
}
}
}

View File

@@ -0,0 +1,13 @@
using Faker;
namespace Showcase.WPF.DragDrop.Models
{
public class DataGridRowModel
{
public string Name { get; set; } = Faker.Name.FullName(NameFormats.Standard);
public string StreetName { get; set; } = Faker.Address.StreetName();
public string City { get; set; } = Faker.Address.City();
}
}

View File

@@ -0,0 +1,18 @@
using System.Windows;
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Models
{
public class DragAdornerTemplateSelector : DataTemplateSelector
{
public DataTemplate TemplateEven { get; set; }
public DataTemplate TemplateOdd { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var itemModel = item as ItemModel;
return itemModel != null && (itemModel.Index & 0x01) == 0 ? TemplateEven : TemplateOdd;
}
}
}

View File

@@ -0,0 +1,41 @@
using System.ComponentModel;
using System.Linq;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
/// <summary>
/// Custom drop handler which is used for the grouping example.
/// </summary>
public class GroupedDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
// Call default DragOver method, cause most stuff should work by default
GongSolutions.Wpf.DragDrop.DragDrop.DefaultDropHandler.DragOver(dropInfo);
if (dropInfo.TargetGroup == null)
{
dropInfo.Effects = System.Windows.DragDropEffects.None;
}
}
public void Drop(IDropInfo dropInfo)
{
// The default drop handler don't know how to set an item's group. You need to explicitly set the group on the dropped item like this.
GongSolutions.Wpf.DragDrop.DragDrop.DefaultDropHandler.Drop(dropInfo);
// Now extract the dragged group items and set the new group (target)
var data = DefaultDropHandler.ExtractData(dropInfo.Data).OfType<GroupedItem>().ToList();
foreach (var groupedItem in data)
{
groupedItem.Group = dropInfo.TargetGroup.Name.ToString();
}
// Changing group data at runtime isn't handled well: force a refresh on the collection view.
if (dropInfo.TargetCollection is ICollectionView)
{
((ICollectionView)dropInfo.TargetCollection).Refresh();
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.Models
{
public class GroupedItem : INotifyPropertyChanged
{
private string _caption;
private string _group;
public GroupedItem(int group, int item)
{
this.Caption = $"Item {item} from Group {group}";
this._group = $"Group {group}";
}
public string Caption
{
get { return _caption; }
set
{
if (value == _caption) return;
_caption = value;
OnPropertyChanged();
}
}
public string Group
{
get { return _group; }
set
{
if (value == _group)
{
return;
}
_group = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,155 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using JetBrains.Annotations;
using Showcase.WPF.DragDrop.ViewModels;
namespace Showcase.WPF.DragDrop.Models
{
public class ItemModel : INotifyPropertyChanged
{
private double _bindableDoubleValue;
private string _selectedSubItem;
public ItemModel()
{
this.BindableDoubleValue = Faker.RandomNumber.Next(0, 100);
for (int i = 0; i < Faker.RandomNumber.Next(2, 20); i++)
{
SubItemCollection.Add(new SubItemModel($"Sub item {i}"));
}
}
public ItemModel(int itemIndex)
: this()
{
this.Index = itemIndex;
this.Caption = $"Item {itemIndex}";
}
public int Index { get; set; }
public string Caption { get; set; }
public ObservableCollection<SubItemModel> SubItemCollection { get; set; } = new ObservableCollection<SubItemModel>();
public string SelectedSubItem
{
get { return _selectedSubItem; }
set
{
if (value == _selectedSubItem) return;
_selectedSubItem = value;
OnPropertyChanged();
}
}
public double BindableDoubleValue
{
get { return _bindableDoubleValue; }
set
{
if (value.Equals(_bindableDoubleValue)) return;
_bindableDoubleValue = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SubItemModel : INotifyPropertyChanged
{
private string _bindableValue;
private bool _bindableOptionA;
private bool _bindableOptionB;
public SubItemModel(string caption)
{
this.Caption = caption;
this.ButtonTestCommand = new SimpleCommand(o => { this.BindableValue = $"Button clicked at {DateTime.UtcNow.ToLocalTime()}"; });
}
public string Caption { get; set; }
public ICommand ButtonTestCommand { get; set; }
public string BindableValue
{
get { return _bindableValue; }
set
{
if (value == _bindableValue) return;
_bindableValue = value;
OnPropertyChanged();
}
}
public bool BindableOptionA
{
get { return _bindableOptionA; }
set
{
if (value == _bindableOptionA) return;
_bindableOptionA = value;
OnPropertyChanged();
}
}
public bool BindableOptionB
{
get { return _bindableOptionB; }
set
{
if (value == _bindableOptionB) return;
_bindableOptionB = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
[Serializable]
public class SerializableItemModel
{
public SerializableItemModel(int itemIndex)
{
this.Index = itemIndex;
this.Caption = $"{itemIndex}. Item";
}
public int Index { get; set; }
public string Caption { get; set; }
public override string ToString()
{
return this.Caption;
}
}
}

View File

@@ -0,0 +1,31 @@
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class ListBoxCustomDropHandler : DefaultDropHandler
{
public override void DragOver(IDropInfo dropInfo)
{
if (dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource)
{
dropInfo.NotHandled = dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource;
}
else
{
base.DragOver(dropInfo);
}
}
public override void Drop(IDropInfo dropInfo)
{
if (dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource)
{
dropInfo.NotHandled = dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource;
}
else
{
base.Drop(dropInfo);
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class NestedDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
if (dropInfo.TargetItem?.ToString().StartsWith("Root", StringComparison.OrdinalIgnoreCase) == true)
{
dropInfo.Effects = DragDropEffects.Move;
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
}
}
public void Drop(IDropInfo dropInfo)
{
// nothing
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class SampleData
{
public SampleData()
{
for (var n = 0; n < 50; ++n)
{
this.SerializableCollection1.Add(new SerializableItemModel(n + 1));
this.Collection1.Add(new ItemModel(n + 1));
this.FilterCollection1.Add(new ItemModel(n + 1));
this.ClonableCollection1.Add(new ClonableItemModel(n + 1));
this.DataGridCollection1.Add(new DataGridRowModel());
}
for (var n = 0; n < 10; ++n)
{
this.Collection4.Add(new ItemModel() { Caption = $"Model {n + 1}" });
}
for (var g = 0; g < 4; ++g)
{
for (var i = 0; i < ((g % 2) == 0 ? 4 : 2); ++i)
{
this.GroupedCollection.Add(new GroupedItem(g, i));
}
}
this.GroupedItemsCollectionViewSource = CollectionViewSource.GetDefaultView(this.GroupedCollection);
this.GroupedItemsCollectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription() { PropertyName = nameof(GroupedItem.Group) });
for (int r = 1; r <= 6; r++)
{
var root = new TreeNode($"Root {r}");
for (var i = 0; i < ((r % 2) == 0 ? 8 : 3); ++i)
{
root.Children.Add(new TreeNode($"Item {i + 10 * r}"));
}
this.TreeCollection1.Add(root);
if (r == 2)
{
root.IsExpanded = true;
}
}
for (int i = 0; i < 5; i++)
{
this.TabItemCollection1.Add(new TabItemModel(i + 1));
}
this.TabItemCollection2.Add(new TabItemModel(1));
}
public ObservableCollection<SerializableItemModel> SerializableCollection1 { get; set; } = new ObservableCollection<SerializableItemModel>();
public ObservableCollection<SerializableItemModel> SerializableCollection2 { get; set; } = new ObservableCollection<SerializableItemModel>();
public SerializableDragHandler SerializableDragHandler { get; set; } = new SerializableDragHandler();
public SerializableDropHandler SerializableDropHandler { get; set; } = new SerializableDropHandler();
public ObservableCollection<ItemModel> Collection1 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection2 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection3 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection4 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ClonableItemModel> ClonableCollection1 { get; set; } = new ObservableCollection<ClonableItemModel>();
public ObservableCollection<ClonableItemModel> ClonableCollection2 { get; set; } = new ObservableCollection<ClonableItemModel>();
public ObservableCollection<ItemModel> FilterCollection1 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> FilterCollection2 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<TreeNode> TreeCollection1 { get; set; } = new ObservableCollection<TreeNode>();
public ObservableCollection<TreeNode> TreeCollection2 { get; set; } = new ObservableCollection<TreeNode>();
public GroupedDropHandler GroupedDropHandler { get; set; } = new GroupedDropHandler();
public ObservableCollection<GroupedItem> GroupedCollection { get; set; } = new ObservableCollection<GroupedItem>();
public ICollectionView GroupedItemsCollectionViewSource { get; }
public ObservableCollection<DataGridRowModel> DataGridCollection1 { get; set; } = new ObservableCollection<DataGridRowModel>();
public ObservableCollection<DataGridRowModel> DataGridCollection2 { get; set; } = new ObservableCollection<DataGridRowModel>();
public ObservableCollection<TabItemModel> TabItemCollection1 { get; set; } = new ObservableCollection<TabItemModel>();
public ObservableCollection<TabItemModel> TabItemCollection2 { get; set; } = new ObservableCollection<TabItemModel>();
public TextBoxCustomDropHandler TextBoxCustomDropHandler { get; set; } = new TextBoxCustomDropHandler();
public ListBoxCustomDropHandler ListBoxCustomDropHandler { get; set; } = new ListBoxCustomDropHandler();
public IDropTarget NestedDropHandler { get; set; } = new NestedDropHandler();
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace Showcase.WPF.DragDrop.Models
{
[Serializable]
public class SerializableWrapper
{
public IEnumerable<object> Items { get; set; }
public DragDropKeyStates DragDropCopyKeyState { get; set; }
}
public class SerializableDragHandler : IDragSource
{
private bool alreadyDropped = false;
public void StartDrag(IDragInfo dragInfo)
{
alreadyDropped = false;
var items = dragInfo.SourceItems.OfType<object>().ToList();
var wrapper = new SerializableWrapper()
{
Items = items,
DragDropCopyKeyState = DragDropKeyStates.ControlKey //dragInfo.DragDropCopyKeyState
};
dragInfo.Data = wrapper;
dragInfo.DataFormat = DataFormats.GetDataFormat(DataFormats.Serializable);
dragInfo.Effects = dragInfo.Data != null ? DragDropEffects.Copy | DragDropEffects.Move : DragDropEffects.None;
}
public bool CanStartDrag(IDragInfo dragInfo)
{
return true;
}
public void Dropped(IDropInfo dropInfo)
{
alreadyDropped = true;
}
public void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo)
{
if (alreadyDropped || dragInfo == null)
{
return;
}
// the drag operation has finished on another app
if (operationResult != DragDropEffects.None)
{
if (operationResult.HasFlag(DragDropEffects.Move))
{
var sourceList = dragInfo.SourceCollection.TryGetList();
var items = dragInfo.SourceItems.OfType<object>().ToList();
if (sourceList != null)
{
foreach (var o in items)
{
sourceList.Remove(o);
}
}
alreadyDropped = true;
}
}
}
public void DragCancelled()
{
}
public bool TryCatchOccurredException(Exception exception)
{
return false;
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace Showcase.WPF.DragDrop.Models
{
public class SerializableDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
var wrapper = GetSerializableWrapper(dropInfo);
if (wrapper != null && dropInfo.TargetCollection != null)
{
dropInfo.Effects = ShouldCopyData(dropInfo, wrapper.DragDropCopyKeyState) ? DragDropEffects.Copy : DragDropEffects.Move;
dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
}
}
public void Drop(IDropInfo dropInfo)
{
var wrapper = GetSerializableWrapper(dropInfo);
if (wrapper != null && dropInfo.TargetCollection != null)
{
// at this point the drag info can be null, cause the other app doesn't know it
var insertIndex = dropInfo.InsertIndex != dropInfo.UnfilteredInsertIndex ? dropInfo.UnfilteredInsertIndex : dropInfo.InsertIndex;
var destinationList = dropInfo.TargetCollection.TryGetList();
var copyData = ShouldCopyData(dropInfo, wrapper.DragDropCopyKeyState);
if (!copyData)
{
var sourceList = dropInfo.DragInfo?.SourceCollection?.TryGetList();
if (sourceList != null)
{
foreach (var o in wrapper.Items)
{
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)
{
// check for cloning
var cloneData = dropInfo.Effects.HasFlag(DragDropEffects.Copy)
|| dropInfo.Effects.HasFlag(DragDropEffects.Link);
foreach (var o in wrapper.Items)
{
var obj2Insert = o;
if (cloneData)
{
var cloneable = o as ICloneable;
if (cloneable != null)
{
obj2Insert = cloneable.Clone();
}
}
destinationList.Insert(insertIndex++, obj2Insert);
}
}
}
}
private static SerializableWrapper GetSerializableWrapper(IDropInfo dropInfo)
{
var data = dropInfo.Data;
var dataObject = data as DataObject;
if (dataObject != null)
{
var dataFormat = DataFormats.GetDataFormat(DataFormats.Serializable);
data = dataObject.GetDataPresent(dataFormat.Name) ? dataObject.GetData(dataFormat.Name) : data;
}
var wrapper = data as SerializableWrapper;
return wrapper;
}
private static bool ShouldCopyData(IDropInfo dropInfo, DragDropKeyStates dragDropCopyKeyState)
{
// default should always the move action/effect
if (dropInfo == null)
{
return false;
}
var copyData = ((dragDropCopyKeyState != default(DragDropKeyStates)) && dropInfo.KeyStates.HasFlag(dragDropCopyKeyState))
|| dragDropCopyKeyState.HasFlag(DragDropKeyStates.LeftMouseButton);
return copyData;
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Showcase.WPF.DragDrop.Models
{
public class TabItemModel
{
public TabItemModel(int itemIndex)
{
this.Header = $"TabItem {itemIndex}";
this.Content = Faker.Lorem.Paragraph();
}
public string Header { get; set; }
public string Content { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class TextBoxCustomDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
dropInfo.DropTargetAdorner = typeof(DropTargetHighlightAdorner);
dropInfo.Effects = DragDropEffects.Move;
}
public void Drop(IDropInfo dropInfo)
{
var dataAsList = DefaultDropHandler.ExtractData(dropInfo.Data);
((TextBox)dropInfo.VisualTarget).Text = string.Join(", ", dataAsList.OfType<object>().ToArray());
}
}
public class DropTargetHighlightAdorner : DropTargetAdorner
{
private readonly Pen _pen;
private readonly Brush _brush;
public DropTargetHighlightAdorner(UIElement adornedElement, DropInfo dropInfo)
: base(adornedElement, dropInfo)
{
_pen = new Pen(Brushes.Tomato, 2);
_pen.Freeze();
_brush = new SolidColorBrush(Colors.Coral) { Opacity = 0.2 };
this._brush.Freeze();
this.SetValue(SnapsToDevicePixelsProperty, true);
}
protected override void OnRender(DrawingContext drawingContext)
{
var visualTarget = this.DropInfo.VisualTarget;
if (visualTarget != null)
{
var translatePoint = visualTarget.TranslatePoint(new Point(), this.AdornedElement);
translatePoint.Offset(1, 1);
var bounds = new Rect(translatePoint,
new Size(visualTarget.RenderSize.Width - 2, visualTarget.RenderSize.Height - 2));
drawingContext.DrawRectangle(_brush, _pen, bounds);
}
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.Models
{
public class TreeNode : INotifyPropertyChanged, ICloneable
{
private string _caption;
private ObservableCollection<TreeNode> _children;
private bool _isCloned;
private bool _isExpanded;
public TreeNode(string caption)
{
this.Caption = caption;
this.Children = new ObservableCollection<TreeNode>();
}
public string Caption
{
get { return _caption; }
set
{
if (value == _caption) return;
_caption = value;
OnPropertyChanged();
}
}
public ObservableCollection<TreeNode> Children
{
get { return _children; }
set
{
if (Equals(value, _children)) return;
_children = value;
OnPropertyChanged();
}
}
public bool IsCloned
{
get { return _isCloned; }
set
{
if (value == _isCloned) return;
_isCloned = value;
OnPropertyChanged();
}
}
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value == _isExpanded) return;
_isExpanded = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public object Clone()
{
var treeNode = new TreeNode(this.Caption) { IsCloned = true };
foreach (var child in this.Children)
{
treeNode.Children.Add((TreeNode)child.Clone());
}
return treeNode;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,6 @@
using System.Runtime.InteropServices;
// 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)]

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<!-- Project properties -->
<PropertyGroup>
<TargetFrameworks>net45;net46;netcoreapp3.0</TargetFrameworks>
<AssemblyName>Showcase.WPF.DragDrop</AssemblyName>
<RootNamespace>Showcase.WPF.DragDrop</RootNamespace>
<DisableFody Condition="'$(Configuration)' == 'Debug' or '$(TargetFramework)' == 'netcoreapp3.0'">true</DisableFody>
<Description>Showcase application for GongSolutions.WPF.DragDrop</Description>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<NoWarn>SA1652</NoWarn>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<!-- reference includes -->
<ItemGroup>
<PackageReference Include="Fody" Version="6.0.0" PrivateAssets="All" />
<PackageReference Include="Costura.Fody" Version="4.*" PrivateAssets="All" />
<PackageReference Include="MahApps.Metro.IconPacks" Version="3.0.0-alpha*" />
<PackageReference Include="Faker.Net" Version="1.*" />
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
<None Remove="App.config" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.0'">
<AppConfigWithTargetPath Include="App.$(TargetFramework).config">
<TargetPath>$(AssemblyName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GongSolutions.WPF.DragDrop\GongSolutions.WPF.DragDrop.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,132 @@
using System.Diagnostics;
using System.Windows.Data;
using System.Windows.Input;
using Showcase.WPF.DragDrop.Models;
namespace Showcase.WPF.DragDrop.ViewModels
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class ListMember : List<SubMember>
{
public string Name { get; set; }
}
public class SubMember
{
public string Name { get; set; }
}
public class MainViewModel : ViewModelBase
{
private SampleData _data;
private ICommand _openIssueCommand;
private ICommand _openPullRequestCommand;
private ICommand _openLinkCommand;
private ICommand _filterCollectionCommand;
public ObservableCollection<ListMember> Members { get; set; } = new ObservableCollection<ListMember>();
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
var listMember = new ListMember() { Name = "Item 1" };
Members.Add(listMember);
listMember = new ListMember() { Name = "Item 2 with SubItems" };
listMember.Add(new SubMember() { Name = "SubItem 1" });
listMember.Add(new SubMember() { Name = "SubItem 2" });
Members.Add(listMember);
listMember = new ListMember() { Name = "Item 3" };
Members.Add(listMember);
this.Data = new SampleData();
this.OpenIssueCommand = new SimpleCommand(issue => { Process.Start($"https://github.com/punker76/gong-wpf-dragdrop/issues/{issue}"); });
this.OpenPullRequestCommand = new SimpleCommand(pr => { Process.Start($"https://github.com/punker76/gong-wpf-dragdrop/pull/{pr}"); });
this.OpenLinkCommand = new SimpleCommand(link => { Process.Start(link.ToString()); });
this.FilterCollectionCommand = new SimpleCommand(isChecked =>
{
var coll = Data.FilterCollection1;
var collView = CollectionViewSource.GetDefaultView(coll);
collView.Filter += o =>
{
if (!(isChecked as bool?).GetValueOrDefault())
{
return true;
}
var itemModel = (ItemModel)o;
var number = itemModel.Index;
return (number & 0x01) == 0;
};
});
}
public SampleData Data
{
get { return _data; }
set
{
if (Equals(value, _data)) return;
_data = value;
OnPropertyChanged();
}
}
public ICommand OpenIssueCommand
{
get { return _openIssueCommand; }
set
{
if (Equals(value, _openIssueCommand)) return;
_openIssueCommand = value;
OnPropertyChanged();
}
}
public ICommand OpenPullRequestCommand
{
get { return _openPullRequestCommand; }
set
{
if (Equals(value, _openPullRequestCommand)) return;
_openPullRequestCommand = value;
OnPropertyChanged();
}
}
public ICommand OpenLinkCommand
{
get { return _openLinkCommand; }
set
{
if (Equals(value, _openLinkCommand)) return;
_openLinkCommand = value;
OnPropertyChanged();
}
}
public ICommand FilterCollectionCommand
{
get { return _filterCollectionCommand; }
set
{
if (Equals(value, _filterCollectionCommand)) return;
_filterCollectionCommand = value;
OnPropertyChanged();
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Windows.Input;
namespace Showcase.WPF.DragDrop.ViewModels
{
public class SimpleCommand : ICommand
{
public SimpleCommand(Action<object> execute = null, Predicate<object> canExecute = null)
{
this.CanExecuteDelegate = canExecute;
this.ExecuteDelegate = execute;
}
public Predicate<object> CanExecuteDelegate { get; set; }
public Action<object> ExecuteDelegate { get; set; }
public bool CanExecute(object parameter)
{
var canExecute = this.CanExecuteDelegate;
return canExecute == null || canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.ExecuteDelegate?.Invoke(parameter);
}
}
}

View File

@@ -0,0 +1,17 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,65 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.DataGridSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultDataGridStyle}" TargetType="{x:Type DataGrid}" />
</Grid.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="DataGrid" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two DataGrids bound to two collections of the same type. Just try drag and drop some Rows from left DataGrid to right DataGrid." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<DataGrid x:Name="LeftBoundDataGrid"
Grid.Column="0"
dd:DragDrop.DropScrollingMode="VerticalOnly"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
ItemsSource="{Binding Data.DataGridCollection1}" />
<DataGrid Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
ItemsSource="{Binding Data.DataGridCollection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left DataGrid)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for DataGridSamples.xaml
/// </summary>
public partial class DataGridSamples : UserControl
{
public DataGridSamples()
{
InitializeComponent();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for Issues.xaml
/// </summary>
public partial class Issues : UserControl
{
public Issues()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,460 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.ListBoxSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultListBoxStyle}" TargetType="{x:Type ListBox}" />
</Grid.Resources>
<TabControl Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="Bound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox Bound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two ListBoxes bound to two collections of the same type. Just try drag and drop some Items from left ListBox to right ListBox." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftBoundListBox"
Grid.Column="0"
dd:DragDrop.CanDragWithMouseRightButton="True"
dd:DragDrop.DropTargetAdornerBrush="Coral"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
<ListBox Grid.Column="1"
dd:DragDrop.DropTargetAdornerBrush="Coral"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
ItemsSource="{Binding Data.Collection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="CanDragWithMouseRightButton"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.CanDragWithMouseRightButton), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Unbound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox Unbound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two ListBoxes and unbound items (e.g. direct usage of ListBoxItem). Just try drag and drop some Items from left ListBox to right ListBox." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftUnboundListBox"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True">
<ListBoxItem>Unbound Item1</ListBoxItem>
<ListBoxItem>Unbound Item2</ListBoxItem>
<ListBoxItem>Unbound Item3</ListBoxItem>
<ListBoxItem>Unbound Item4</ListBoxItem>
<ListBoxItem>Unbound Item5</ListBoxItem>
</ListBox>
<ListBox Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True">
<ListBoxItem>Unbound Item6</ListBoxItem>
<ListBoxItem>Unbound Item7</ListBoxItem>
<ListBoxItem>Unbound Item8</ListBoxItem>
<ListBoxItem>Unbound Item9</ListBoxItem>
<ListBoxItem>Unbound Item10</ListBoxItem>
</ListBox>
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftUnboundListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftUnboundListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftUnboundListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftUnboundListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Horizontal">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox Horizontal" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with horizontal ListBoxes and right to left flow direction." />
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Unbound ListBox" />
<ListBox Height="Auto"
dd:DragDrop.DefaultDragAdornerOpacity="0.5"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
SelectionMode="Extended">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>ListBoxItem1</ListBoxItem>
<ListBoxItem>ListBoxItem2</ListBoxItem>
<ListBoxItem>ListBoxItem3</ListBoxItem>
<ListBoxItem>ListBoxItem4</ListBoxItem>
<ListBoxItem>ListBoxItem5</ListBoxItem>
</ListBox>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="ListBox bound to a collection" />
<ListBox Height="Auto"
dd:DragDrop.DefaultDragAdornerOpacity="0.5"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection1}"
SelectionMode="Extended">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="ListBox with right to left flow direction for the ItemsPanel" />
<ListBox Height="Auto"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
SelectionMode="Extended">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1"
FlowDirection="RightToLeft"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBoxItem>ListBoxItem6</ListBoxItem>
<ListBoxItem>ListBoxItem7</ListBoxItem>
<ListBoxItem>ListBoxItem8</ListBoxItem>
<ListBoxItem>ListBoxItem9</ListBoxItem>
<ListBoxItem>ListBoxItem10</ListBoxItem>
</ListBox>
<TextBlock Margin="4 8 4 4"
Style="{StaticResource DefaultTextBlockStyle}"
Text="FlowDirection=&quot;RightToLeft&quot; for the ItemsPanel doesn't work in this case! The insertion adorner will be wrong calculated." />
<ListBox Height="Auto"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemContainerStyle="{x:Null}"
SelectionMode="Extended">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1"
FlowDirection="RightToLeft"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<Rectangle Width="100"
Height="60"
Fill="LightCoral"
Stroke="Black" />
<Rectangle Width="120"
Height="60"
Fill="LightCyan"
Stroke="Black" />
<Rectangle Width="140"
Height="60"
Fill="LightGreen"
Stroke="Black" />
<Rectangle Width="160"
Height="60"
Fill="LightPink"
Stroke="Black" />
</ListBox>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Set RotateTransform of ListBox to ScaleTransform ScaleX=&quot;-1&quot;." />
<ListBox Height="Auto"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemContainerStyle="{x:Null}"
RenderTransformOrigin="0.5,0.5"
SelectionMode="Extended">
<ListBox.RenderTransform>
<ScaleTransform ScaleX="-1" />
</ListBox.RenderTransform>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="1" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<Rectangle Width="100"
Height="60"
Fill="LightCoral"
Stroke="Black" />
<Rectangle Width="120"
Height="60"
Fill="LightCyan"
Stroke="Black" />
<Rectangle Width="140"
Height="60"
Fill="LightGreen"
Stroke="Black" />
<Rectangle Width="160"
Height="60"
Fill="LightPink"
Stroke="Black" />
</ListBox>
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Grouping">
<TabItem.Resources>
<CollectionViewSource x:Key="GroupedCollectionViewSource" Source="{Binding Data.GroupedCollection}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Group" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox Grouping" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the behaviour with a ListBox which is bound to a grouped collection (CollectionViewSource). The ListBox uses also a custom DropHandler to handle the drop action." />
<ListBox x:Name="GroupedListBox"
dd:DragDrop.DragDirectlySelectedOnly="True"
dd:DragDrop.DropHandler="{Binding Data.GroupedDropHandler}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
DisplayMemberPath="Caption"
ItemsSource="{Binding Source={StaticResource GroupedCollectionViewSource}}"
SelectionMode="Extended">
<ListBox.GroupStyle>
<x:Static Member="GroupStyle.Default" />
</ListBox.GroupStyle>
</ListBox>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=GroupedListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=GroupedListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=GroupedListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=GroupedListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="DragDropKeyStates">
<TabItem.Resources>
<ObjectDataProvider x:Key="DragDropKeyStatesEnumValues"
MethodName="GetValues"
ObjectType="{x:Type DragDropKeyStates}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="DragDropKeyStates" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox DragDropKeyStates" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the DragDropCopyKeyState behaviour with two ListBoxes bound to two collections of the same type. Try changing the DragDropCopyKeyState for the left ListBox and see what happens." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftListBoxOfDragDropKeyStatesSample"
Grid.Column="0"
dd:DragDrop.DragDropCopyKeyState="ControlKey"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
<ListBox Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemsSource="{Binding Data.Collection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<StackPanel Margin="10 5" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="DragDropCopyKeyState" />
<ComboBox Width="200"
Margin="5 0 0 0"
VerticalAlignment="Center"
ItemsSource="{Binding Source={StaticResource DragDropKeyStatesEnumValues}}"
SelectedItem="{Binding ElementName=LeftListBoxOfDragDropKeyStatesSample, Path=(dd:DragDrop.DragDropCopyKeyState), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftListBoxOfDragDropKeyStatesSample, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftListBoxOfDragDropKeyStatesSample, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftListBoxOfDragDropKeyStatesSample, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftListBoxOfDragDropKeyStatesSample, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="List in List">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox List in List" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="This sample comes from an issue and demonstrates the behaviour of nested ListBoxes." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
Height="450"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection1}" />
<ListBox x:Name="Outer"
Grid.Column="1"
Height="450"
dd:DragDrop.IsDropTarget="True">
<Border BorderBrush="Silver" BorderThickness="1">
<ListBox x:Name="Inner1"
Width="200"
Height="150"
HorizontalContentAlignment="Stretch"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
Background="DarkOrange"
ItemsSource="{Binding Data.Collection2}" />
</Border>
<Border BorderBrush="Silver" BorderThickness="1">
<ListBox x:Name="Inner2"
Width="200"
Height="150"
HorizontalContentAlignment="Stretch"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
Background="DarkSeaGreen">
<ListBoxItem Content="ListBoxItem1" />
<ListBoxItem Content="ListBoxItem2" />
</ListBox>
</Border>
</ListBox>
</Grid>
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Undraggable List in List">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListBox Undraggable List in List" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="This sample comes from an issue and demonstrates the behaviour of nested ListBoxes that can be dragged in the outer ListBox while their own items cannot be dragged" />
<ListBox x:Name="Outer2"
Height="450"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True">
<Border BorderBrush="Silver" BorderThickness="1">
<ListBox x:Name="Inner3"
Width="200"
Height="150"
HorizontalContentAlignment="Stretch"
Background="HotPink">
<ListBoxItem Content="ListBoxItem1 Inner3" />
<ListBoxItem Content="ListBoxItem2 Inner3" />
</ListBox>
</Border>
<Border BorderBrush="Silver" BorderThickness="1">
<ListBox x:Name="Inner4"
Width="200"
Height="150"
HorizontalContentAlignment="Stretch"
Background="LightSkyBlue">
<ListBoxItem Content="ListBoxItem1 Inner4" />
<ListBoxItem Content="ListBoxItem2 Inner4" />
<ListBoxItem Content="ListBoxItem3 Inner4" />
</ListBox>
</Border>
</ListBox>
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for ListBoxSample.xaml
/// </summary>
public partial class ListBoxSamples : UserControl
{
public ListBoxSamples()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,223 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.ListViewSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultListViewStyle}" TargetType="{x:Type ListView}" />
</Grid.Resources>
<TabControl Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="Bound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListView Bound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two ListViews bound to two collections of the same type. Just try drag and drop some Items from left ListView to right ListView." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView x:Name="LeftBoundListView"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
ItemsSource="{Binding Data.Collection1}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<iconPacks:PackIconMaterial Focusable="False"
Foreground="Gray"
Kind="File" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="ContextMenu Item 1" />
<MenuItem Header="ContextMenu Item 2" />
<MenuItem Header="ContextMenu Item 3" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ListView Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection2}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" SharedSizeGroup="CaptionColum" />
</Grid.ColumnDefinitions>
<iconPacks:PackIconMaterial Grid.Column="0"
Focusable="False"
Foreground="Gray"
Kind="FileDocument" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
Grid.IsSharedSizeScope="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListView)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundListView, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundListView, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundListView, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundListView, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Unbound">
<TabItem.Resources>
<Style x:Key="CustomLeftUnboundListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" SharedSizeGroup="CaptionColum" />
</Grid.ColumnDefinitions>
<iconPacks:PackIconMaterial Grid.Column="0"
Focusable="False"
Foreground="Gray"
Kind="File" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2" />
</Style>
<Style x:Key="CustomRightUnboundListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" SharedSizeGroup="CaptionColum" />
</Grid.ColumnDefinitions>
<iconPacks:PackIconMaterial Grid.Column="0"
Focusable="False"
Foreground="Gray"
Kind="FileDocument" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2" />
</Style>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="ListView Unbound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two ListViews and unbound items (e.g. direct usage of ListViewItem). Just try drag and drop some Items from left ListView to right ListView." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView x:Name="LeftUnboundListView"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemContainerStyle="{StaticResource CustomLeftUnboundListViewItemStyle}">
<ListViewItem>Unbound Item1</ListViewItem>
<ListViewItem>Unbound Item2</ListViewItem>
<ListViewItem>Unbound Item3</ListViewItem>
<ListViewItem>Unbound Item4</ListViewItem>
<ListViewItem>Unbound Item5</ListViewItem>
</ListView>
<ListView Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemContainerStyle="{StaticResource CustomRightUnboundListViewItemStyle}">
<ListViewItem>Unbound Item6</ListViewItem>
<ListViewItem>Unbound Item7</ListViewItem>
<ListViewItem>Unbound Item8</ListViewItem>
<ListViewItem>Unbound Item9</ListViewItem>
<ListViewItem>Unbound Item10</ListViewItem>
</ListView>
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListView)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftUnboundListView, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftUnboundListView, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftUnboundListView, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftUnboundListView, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for ListViewSample.xaml
/// </summary>
public partial class ListViewSamples : UserControl
{
public ListViewSamples()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,603 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.MixedSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:Showcase.WPF.DragDrop.Models"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultItemsControl}" TargetType="{x:Type ItemsControl}" />
<Style BasedOn="{StaticResource DefaultListBoxStyle}" TargetType="{x:Type ListBox}" />
<Style BasedOn="{StaticResource DefaultListViewStyle}" TargetType="{x:Type ListView}" />
<Style BasedOn="{StaticResource DefaultTreeViewStyle}" TargetType="{x:Type TreeView}" />
</Grid.Resources>
<TabControl x:Name="MixedTabControl" Style="{StaticResource ShowcaseTabControlStyle}">
<TabControl.Resources>
<DataTemplate x:Key="DragAdorner">
<Border Background="Silver"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="2"
SnapsToDevicePixels="True">
<StackPanel Margin="4" Orientation="Horizontal">
<iconPacks:PackIconMaterial VerticalAlignment="Center" Kind="MoveResizeVariant" />
<TextBlock Margin="6 0 0 0"
VerticalAlignment="Center"
Text="{Binding}" />
</StackPanel>
</Border>
</DataTemplate>
</TabControl.Resources>
<TabItem Header="DragAdorner">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="DragAdorner" />
<UniformGrid Columns="2" Rows="1">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="ListBox" />
<ListBox Height="Auto"
dd:DragDrop.DragAdornerTemplate="{StaticResource DragAdorner}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="ListView" />
<ListView Height="Auto"
dd:DragDrop.DragAdornerTemplate="{StaticResource DragAdorner}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection2}" />
</DockPanel>
</UniformGrid>
</DockPanel>
</TabItem>
<TabItem Header="DragDropContext">
<TabItem.Resources>
<Style BasedOn="{StaticResource DefaultListBoxStyle}" TargetType="{x:Type ListBox}">
<Setter Property="Height" Value="Auto" />
</Style>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="DragDropContext sample" />
<UniformGrid Columns="2" Rows="2">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="DragDropContext = MainContext" />
<ListBox Height="Auto"
dd:DragDrop.DragDropContext="MainContext"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="DragDropContext = MainContext" />
<ListBox Height="Auto"
dd:DragDrop.DragDropContext="MainContext"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection2}" />
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="DragDropContext = DifferentContext" />
<ListBox Height="Auto"
dd:DragDrop.DragDropContext="DifferentContext"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection2}" />
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="No DragDropContext" />
<ListBox Height="Auto"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection4}" />
</DockPanel>
</UniformGrid>
</DockPanel>
</TabItem>
<TabItem Header="Filtered">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="Filtered ListBox" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the behaviour with a filtered and unfiltered ListBox bound to two collections of the same type." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftFilterListBox"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.FilterCollection1}" />
<ListBox Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemsSource="{Binding Data.FilterCollection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Command="{Binding FilterCollectionCommand, Mode=OneWay}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}"
Content="Filter by %2" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftFilterListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftFilterListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftFilterListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftFilterListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Custom styled ItemsControl">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="Custom styled ItemsControl" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="A custom styles ItemsControl on the left side. It's here to check if it works... yes, really." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection1}">
<ItemsControl.Style>
<Style BasedOn="{StaticResource DefaultItemsControl}" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border Background="WhiteSmoke">
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Style>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="4" Background="Bisque">
<TextBlock Text="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ListBox Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection2}" />
</Grid>
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="DragAdornerTemplateSelector">
<TabItem.Resources>
<DataTemplate x:Key="Even">
<Border Background="Gainsboro"
BorderBrush="Silver"
BorderThickness="1"
CornerRadius="2">
<TextBlock Margin="2"
FontWeight="Bold"
Foreground="Green"
Text="{Binding}" />
</Border>
</DataTemplate>
<DataTemplate x:Key="Odd">
<Border Background="Gainsboro"
BorderBrush="Silver"
BorderThickness="1"
CornerRadius="2">
<TextBlock Margin="2"
FontWeight="Bold"
Foreground="DodgerBlue"
Text="{Binding}" />
</Border>
</DataTemplate>
<models:DragAdornerTemplateSelector x:Key="DragAdornerTemplateSelector"
TemplateEven="{StaticResource Even}"
TemplateOdd="{StaticResource Odd}" />
<DataTemplate x:Key="DropAdornerTemplate">
<Border Background="GhostWhite"
BorderBrush="Black"
BorderThickness="1">
<DockPanel LastChildFill="True">
<iconPacks:PackIconOcticons Margin="4"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Kind="Hubot" />
<TextBlock Margin="4"
FontSize="14"
FontWeight="Bold"
Foreground="Crimson"
Text="{Binding}" />
</DockPanel>
</Border>
</DataTemplate>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="DragAdornerTemplateSelector" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the usage of a DataTemplateSelector for the drag adorner." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftDataTemplateSelectorSampleListBox"
Grid.Column="0"
dd:DragDrop.DragAdornerTemplateSelector="{StaticResource DragAdornerTemplateSelector}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
<ListBox Grid.Column="1"
dd:DragDrop.DropAdornerTemplate="{StaticResource DropAdornerTemplate}"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemsSource="{Binding Data.Collection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftDataTemplateSelectorSampleListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftDataTemplateSelectorSampleListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftDataTemplateSelectorSampleListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Cloning">
<TabItem.Resources>
<Style x:Key="ClonableListBoxItemStyle"
BasedOn="{StaticResource DefaultListBoxItemStyle}"
TargetType="{x:Type ListBoxItem}">
<Setter Property="dd:DragDrop.DragSourceIgnore" Value="True" />
</Style>
<DataTemplate x:Key="CloningTemplate" DataType="{x:Type models:ClonableItemModel}">
<Border Background="Gainsboro"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="2"
SnapsToDevicePixels="True">
<StackPanel Margin="4" Orientation="Horizontal">
<iconPacks:PackIconMaterial dd:DragDrop.DragSourceIgnore="False"
Background="Transparent"
Cursor="SizeAll"
Kind="Drag" />
<TextBlock Margin="6 0 0 0"
VerticalAlignment="Center"
Text="{Binding Caption}" />
</StackPanel>
</Border>
</DataTemplate>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="Cloning" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the cloning behaviour with 2 ListBoxes bound to collections of the same type. The Model of the collections implements the ICloneable interface. You can clone an Item by holding the CTRL key while drag and drop." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftClonableListBox"
Grid.Column="0"
HorizontalContentAlignment="Stretch"
dd:DragDrop.DragAdornerTemplate="{StaticResource CloningTemplate}"
dd:DragDrop.DragDropCopyKeyState="ControlKey"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemContainerStyle="{StaticResource ClonableListBoxItemStyle}"
ItemTemplate="{StaticResource CloningTemplate}"
ItemsSource="{Binding Data.ClonableCollection1}" />
<ListBox Grid.Column="1"
HorizontalContentAlignment="Stretch"
dd:DragDrop.DragAdornerTemplate="{StaticResource CloningTemplate}"
dd:DragDrop.DragDropCopyKeyState="ControlKey"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemContainerStyle="{StaticResource ClonableListBoxItemStyle}"
ItemTemplate="{StaticResource CloningTemplate}"
ItemsSource="{Binding Data.ClonableCollection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftClonableListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftClonableListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftClonableListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="ItemsControl">
<TabItem.Resources />
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="Another ItemsControl sample" />
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource DefaultTextBlockStyle}"
Text="Demonstrates the behaviour with an ItemsControl that has a DataTemplate with an ItemsControl and Buttons inside." />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl Height="Auto"
dd:DragDrop.DragAdornerTemplate="{StaticResource DragAdorner}"
dd:DragDrop.DragDropContext="OuterContext"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0 1 0 1" Background="Coral">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Margin="2"
FontSize="16"
Text="{Binding}" />
<ItemsControl Grid.Row="1"
Height="Auto"
dd:DragDrop.DragDropContext="InnerContext"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemsSource="{Binding SubItemCollection, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:SubItemModel}">
<Grid Background="#FDA07E">
<StackPanel Orientation="Horizontal">
<Button Margin="20 0 10 0"
VerticalAlignment="Center"
Command="{Binding ButtonTestCommand}"
Content="{Binding Caption}" />
<RadioButton VerticalAlignment="Center"
Content="Option A"
IsChecked="{Binding BindableOptionA}" />
<RadioButton VerticalAlignment="Center"
Content="Option B"
IsChecked="{Binding BindableOptionB}" />
<TextBlock VerticalAlignment="Center" Text="{Binding BindableValue}" />
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</DockPanel>
</TabItem>
<TabItem Header="Outside">
<TabItem.Resources />
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top" Orientation="Vertical">
<TextBlock Style="{StaticResource SampleHeaderTextBlockStyle}" Text="Drag and drop from one App to another one." />
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates drag and drop action bewteen one application to another one. Just open the other app and try to drag one or more items to this app or vice versa." />
<Button Margin="2"
Click="ButtonOpenAnotherAppOnClick"
Content="Open another application..." />
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftBoundListBox"
Grid.Column="0"
Height="Auto"
dd:DragDrop.DragHandler="{Binding Data.SerializableDragHandler}"
dd:DragDrop.DropHandler="{Binding Data.SerializableDropHandler}"
dd:DragDrop.DropTargetAdornerBrush="BlueViolet"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.SerializableCollection1}" />
<ListBox Grid.Column="1"
Height="Auto"
dd:DragDrop.DragHandler="{Binding Data.SerializableDragHandler}"
dd:DragDrop.DropHandler="{Binding Data.SerializableDropHandler}"
dd:DragDrop.DropTargetAdornerBrush="BlueViolet"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.SerializableCollection2}" />
</Grid>
<StackPanel Grid.Row="1">
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="CanDragWithMouseRightButton"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.CanDragWithMouseRightButton), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</DockPanel>
</TabItem>
<TabItem Header="Keep Selection">
<TabItem.Resources />
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top" Orientation="Vertical">
<TextBlock Style="{StaticResource SampleHeaderTextBlockStyle}" Text="Keep selection after Drag and drop action." />
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="LeftBoundKeepSelListBox"
Grid.Column="0"
Height="Auto"
dd:DragDrop.DropTargetAdornerBrush="Crimson"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.SelectDroppedItems="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection1}" />
<ListView Grid.Column="1"
Height="Auto"
dd:DragDrop.DropTargetAdornerBrush="Crimson"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.SelectDroppedItems="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
ItemsSource="{Binding Data.Collection2}">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True">
<iconPacks:PackIconMaterial Margin="2"
Focusable="False"
Foreground="DarkOrange"
Kind="CheckboxMarkedOutline" />
<TextBlock Margin="2" Text="{Binding}" />
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<StackPanel Grid.Row="1">
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left ListBox)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundKeepSelListBox, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundKeepSelListBox, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundKeepSelListBox, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="ShowAlwaysDropTargetAdorner"
IsChecked="{Binding ElementName=LeftBoundKeepSelListBox, Path=(dd:DragDrop.ShowAlwaysDropTargetAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="SelectDroppedItems"
IsChecked="{Binding ElementName=LeftBoundKeepSelListBox, Path=(dd:DragDrop.SelectDroppedItems), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</DockPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,35 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for MixedSamples.xaml
/// </summary>
public partial class MixedSamples : UserControl
{
public MixedSamples()
{
InitializeComponent();
this.Loaded += MainWindowLoaded;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var appArgs = Environment.GetCommandLineArgs();
if (appArgs.Length > 1 && appArgs[1] == "anotherOne")
{
MixedTabControl.SelectedItem = MixedTabControl.Items.OfType<TabItem>().FirstOrDefault(t => (string)t.Header == "Outside");
}
}
private void ButtonOpenAnotherAppOnClick(object sender, RoutedEventArgs e)
{
Process.Start(Assembly.GetEntryAssembly().Location, "anotherOne");
}
}
}

View File

@@ -0,0 +1,144 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.TabControlSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:Showcase.WPF.DragDrop.Models"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultTabControlStyle}" TargetType="{x:Type TabControl}" />
<Style x:Key="CustomTabControlStyle"
BasedOn="{StaticResource DefaultTabControlStyle}"
TargetType="{x:Type TabControl}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style BasedOn="{StaticResource DefaultTabItemStyle}" TargetType="{x:Type TabItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type models:TabItemModel}">
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type models:TabItemModel}">
<TextBlock Margin="2" Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<TabControl Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="Bound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="TabControl Bound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two TabControls bound to two collections of the same type." />
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="It's currently not possible to drag into an empty TabControl." />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl x:Name="TopBoundTabControl"
Grid.Row="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemsSource="{Binding Data.TabItemCollection1}"
Style="{StaticResource CustomTabControlStyle}" />
<TabControl Grid.Row="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
ItemsSource="{Binding Data.TabItemCollection2}"
Style="{StaticResource CustomTabControlStyle}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for top TabControl)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=TopBoundTabControl, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=TopBoundTabControl, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=TopBoundTabControl, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=TopBoundTabControl, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Unbound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="TabControl Unbound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two TabControls and unbound items (e.g. direct usage of TabItem)." />
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="It's currently not possible to drag into an empty TabControl." />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl x:Name="TopUnboundTabControl"
Grid.Row="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True">
<TabItem Header="Unbound Item 1">Content of Unbound Item 1</TabItem>
<TabItem Header="Unbound Item 2">Content of Unbound Item 2</TabItem>
<TabItem Header="Unbound Item 3">Content of Unbound Item 3</TabItem>
<TabItem Header="Unbound Item 4">Content of Unbound Item 4</TabItem>
<TabItem Header="Unbound Item 5">Content of Unbound Item 5</TabItem>
</TabControl>
<TabControl Grid.Row="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True">
<TabItem Header="Unbound Item 6">Content of Unbound Item 6</TabItem>
<TabItem Header="Unbound Item 7">Content of Unbound Item 7</TabItem>
<TabItem Header="Unbound Item 8">Content of Unbound Item 8</TabItem>
<TabItem Header="Unbound Item 9">Content of Unbound Item 9</TabItem>
<TabItem Header="Unbound Item 10">Content of Unbound Item 10</TabItem>
</TabControl>
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for top TabControl)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=TopUnboundTabControl, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=TopUnboundTabControl, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=TopUnboundTabControl, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=TopUnboundTabControl, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for TabControlSamples.xaml
/// </summary>
public partial class TabControlSamples : UserControl
{
public TabControlSamples()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,146 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.TreeViewSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultTreeViewStyle}" TargetType="{x:Type TreeView}" />
</Grid.Resources>
<TabControl Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="Bound">
<TabItem.Resources>
<Style x:Key="BoundTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="Padding" Value="2" />
</Style>
</TabItem.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="TreeView Unbound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two TreeViews bound to two collections of the same type. Just try drag and drop some Items from left TreeView to right TreeView." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TreeView x:Name="LeftBoundTreeView"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
dd:DragDrop.SelectDroppedItems="True"
ItemContainerStyle="{StaticResource BoundTreeViewItemStyle}"
Loaded="LeftBoundTreeView_Loaded"
ItemsSource="{Binding Data.TreeCollection1}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Margin="2" Text="{Binding Caption}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<TreeView Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
ItemContainerStyle="{StaticResource BoundTreeViewItemStyle}"
ItemsSource="{Binding Data.TreeCollection2}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Margin="2" Text="{Binding Caption}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left TreeView)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundTreeView, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundTreeView, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundTreeView, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundTreeView, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem Header="Unbound">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="TreeView Unbound" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two TreeViews and unbound items (e.g. direct usage of TreeViewItem). Just try drag and drop some Items from left TreeView to right TreeView." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TreeView x:Name="LeftUnboundTreeView"
Grid.Column="0"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True">
<TreeViewItem Header="Root 1" IsExpanded="True">
<TreeViewItem Header="Item 10" />
<TreeViewItem Header="Item 20" />
<TreeViewItem Header="Item 30" />
<TreeViewItem Header="Item 40" />
<TreeViewItem Header="Item 50" />
</TreeViewItem>
<TreeViewItem Header="Root 2" IsExpanded="True">
<TreeViewItem Header="Item 20" />
<TreeViewItem Header="Item 21" />
<TreeViewItem Header="Item 22" />
</TreeViewItem>
<TreeViewItem Header="Root 3" />
</TreeView>
<TreeView Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left TreeView)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftUnboundTreeView, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftUnboundTreeView, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftUnboundTreeView, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftUnboundTreeView, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,32 @@
using System.Windows.Controls;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for TreeViewSamples.xaml
/// </summary>
public partial class TreeViewSamples : UserControl
{
public TreeViewSamples()
{
InitializeComponent();
}
private void LeftBoundTreeView_RequestBringIntoView(object sender, System.Windows.RequestBringIntoViewEventArgs e)
{
e.Handled = true;
}
private void LeftBoundTreeView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
// Prevent automatic horizontal and vertical scrolling by subscribing to ScrollContentPresenter's RequestBringIntoView event and flag it as handled.
var p = (sender as TreeView).GetVisualDescendent<ScrollContentPresenter>();
if (p != null)
{
p.RequestBringIntoView -= this.LeftBoundTreeView_RequestBringIntoView;
p.RequestBringIntoView += this.LeftBoundTreeView_RequestBringIntoView;
}
}
}
}

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity version="1.0.0.0" name="Showcase.WPF.DragDrop"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker"
uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of all Windows versions that this application is designed to work with. Windows will automatically select the most compatible environment.-->
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates application support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates application support for Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates application support for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= RS1 (Windows 10 Anniversary Update)
2) System < RS1 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
</asmv1:assembly>

View File

@@ -0,0 +1,5 @@
{
"sdk": {
"version": "3.0"
}
}

Binary file not shown.