From 504a4133b808fe0b253e20cb728055fd86123bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Karger=20=E3=83=84=20=E2=98=80?= Date: Wed, 18 Aug 2021 09:00:25 +0200 Subject: [PATCH 1/7] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..76011ae4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: Bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From ce3bfe6b34aeec67b5f9f1400fb2646262dd8689 Mon Sep 17 00:00:00 2001 From: punker76 Date: Wed, 18 Aug 2021 13:03:06 +0200 Subject: [PATCH 2/7] Update proeprty comments --- .../DragDrop.Properties.cs | 99 ++++++++++--------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs index a0baefc2..dd80cc23 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs @@ -15,7 +15,7 @@ public static partial class DragDrop public static DataFormat DataFormat { get; } = DataFormats.GetDataFormat("GongSolutions.Wpf.DragDrop"); /// - /// Gets or sets the data format which will be used for the drag and drop actions. + /// Gets or sets the data format which will be used for the drag and drop operations. /// public static readonly DependencyProperty DataFormatProperty = DependencyProperty.RegisterAttached("DataFormat", @@ -24,7 +24,7 @@ public static readonly DependencyProperty DataFormatProperty new PropertyMetadata(DragDrop.DataFormat)); /// - /// Gets the data format which will be used for the drag and drop actions. + /// Gets the data format which will be used for the drag and drop operations. /// public static DataFormat GetDataFormat(UIElement source) { @@ -32,7 +32,7 @@ public static DataFormat GetDataFormat(UIElement source) } /// - /// Sets the data format which will be used for the drag and drop actions. + /// Sets the data format which will be used for the drag and drop operations. /// public static void SetDataFormat(UIElement source, DataFormat value) { @@ -40,7 +40,7 @@ public static void SetDataFormat(UIElement source, DataFormat value) } /// - /// Gets or Sets whether the control can be used as drag source. + /// Gets or sets whether the control can be used as drag source. /// public static readonly DependencyProperty IsDragSourceProperty = DependencyProperty.RegisterAttached("IsDragSource", @@ -95,7 +95,7 @@ private static void IsDragSourceChanged(DependencyObject d, DependencyPropertyCh } /// - /// Gets or Sets whether the control can be used as drop target. + /// Gets or sets whether the control can be used as drop target. /// public static readonly DependencyProperty IsDropTargetProperty = DependencyProperty.RegisterAttached("IsDropTarget", @@ -156,10 +156,13 @@ public static void SetDropEventType(DependencyObject obj, EventType value) } /// - /// Gets or sets the events which are subscribed for the drag and drop events + /// Gets or sets the events which are subscribed for the drag and drop events. /// - public static readonly DependencyProperty DropEventTypeProperty = - DependencyProperty.RegisterAttached("DropEventType", typeof(EventType), typeof(DragDrop), new PropertyMetadata(EventType.Auto, DropEventTypeChanged)); + public static readonly DependencyProperty DropEventTypeProperty + = DependencyProperty.RegisterAttached("DropEventType", + typeof(EventType), + typeof(DragDrop), + new PropertyMetadata(EventType.Auto, DropEventTypeChanged)); private static void DropEventTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { @@ -293,7 +296,7 @@ private static void UnregisterDragDropEvents(UIElement uiElement, EventType even } /// - /// Gets or Sets whether the control can be used as drag source together with the right mouse. + /// Gets or sets whether the control can be used as drag source together with the right mouse. /// public static readonly DependencyProperty CanDragWithMouseRightButtonProperty = DependencyProperty.RegisterAttached("CanDragWithMouseRightButton", @@ -349,7 +352,7 @@ private static void CanDragWithMouseRightButtonChanged(DependencyObject d, Depen public static IRootElementFinder DefaultRootElementFinder { get; } = new RootElementFinder(); /// - /// Gets or Sets the handler for the drag action. + /// Gets or sets the handler for the drag operation. /// public static readonly DependencyProperty DragHandlerProperty = DependencyProperty.RegisterAttached("DragHandler", @@ -357,7 +360,7 @@ public static readonly DependencyProperty DragHandlerProperty typeof(DragDrop)); /// - /// Gets the handler for the drag action. + /// Gets the handler for the drag operation. /// public static IDragSource GetDragHandler(UIElement target) { @@ -365,7 +368,7 @@ public static IDragSource GetDragHandler(UIElement target) } /// - /// Sets the handler for the drag action. + /// Sets the handler for the drag operation. /// public static void SetDragHandler(UIElement target, IDragSource value) { @@ -373,7 +376,7 @@ public static void SetDragHandler(UIElement target, IDragSource value) } /// - /// Gets or Sets the handler for the drop action. + /// Gets or sets the handler for the drop operation. /// public static readonly DependencyProperty DropHandlerProperty = DependencyProperty.RegisterAttached("DropHandler", @@ -381,7 +384,7 @@ public static readonly DependencyProperty DropHandlerProperty typeof(DragDrop)); /// - /// Gets the handler for the drop action. + /// Gets the handler for the drop operation. /// public static IDropTarget GetDropHandler(UIElement target) { @@ -389,7 +392,7 @@ public static IDropTarget GetDropHandler(UIElement target) } /// - /// Sets the handler for the drop action. + /// Sets the handler for the drop operation. /// public static void SetDropHandler(UIElement target, IDropTarget value) { @@ -397,7 +400,7 @@ public static void SetDropHandler(UIElement target, IDropTarget value) } /// - /// Gets or Sets the ScrollingMode for the drop action. + /// Gets or sets the ScrollingMode for the drop operation. /// public static readonly DependencyProperty DropScrollingModeProperty = DependencyProperty.RegisterAttached("DropScrollingMode", @@ -406,7 +409,7 @@ public static readonly DependencyProperty DropScrollingModeProperty new PropertyMetadata(ScrollingMode.Both)); /// - /// Gets the ScrollingMode for the drop action. + /// Gets the ScrollingMode for the drop operation. /// public static ScrollingMode GetDropScrollingMode(UIElement target) { @@ -414,7 +417,7 @@ public static ScrollingMode GetDropScrollingMode(UIElement target) } /// - /// Sets the ScrollingMode for the drop action. + /// Sets the ScrollingMode for the drop operation. /// public static void SetDropScrollingMode(UIElement target, ScrollingMode value) { @@ -422,7 +425,7 @@ public static void SetDropScrollingMode(UIElement target, ScrollingMode value) } /// - /// Gets or Sets whether to show the DropTargetAdorner (DropTargetInsertionAdorner) on an empty target too. + /// Gets or sets whether to show the DropTargetAdorner (DropTargetInsertionAdorner) on an empty target too. /// public static readonly DependencyProperty ShowAlwaysDropTargetAdornerProperty = DependencyProperty.RegisterAttached("ShowAlwaysDropTargetAdorner", @@ -447,7 +450,7 @@ public static void SetShowAlwaysDropTargetAdorner(UIElement target, bool value) } /// - /// Gets or Sets the brush for the DropTargetAdorner. + /// Gets or sets the brush for the DropTargetAdorner. /// public static readonly DependencyProperty DropTargetAdornerBrushProperty = DependencyProperty.RegisterAttached("DropTargetAdornerBrush", @@ -472,7 +475,7 @@ public static void SetDropTargetAdornerBrush(UIElement target, Brush value) } /// - /// Gets or Sets a context for a control. Only controls with the same context are allowed for drag or drop actions. + /// Gets or sets a context for a control. Only controls with the same context are allowed for drag or drop actions. /// public static readonly DependencyProperty DragDropContextProperty = DependencyProperty.RegisterAttached("DragDropContext", @@ -497,7 +500,7 @@ public static void SetDragDropContext(UIElement target, string value) } /// - /// Gets or Sets whether an element under the mouse should be ignored for the drag action. + /// Gets or sets whether an element under the mouse should be ignored for the drag operation. /// public static readonly DependencyProperty DragSourceIgnoreProperty = DependencyProperty.RegisterAttached("DragSourceIgnore", @@ -506,7 +509,7 @@ public static readonly DependencyProperty DragSourceIgnoreProperty new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits)); /// - /// Gets whether an element under the mouse should be ignored for the drag action. + /// Gets whether an element under the mouse should be ignored for the drag operation. /// public static bool GetDragSourceIgnore(UIElement source) { @@ -514,7 +517,7 @@ public static bool GetDragSourceIgnore(UIElement source) } /// - /// Sets whether an element under the mouse should be ignored for the drag action. + /// Sets whether an element under the mouse should be ignored for the drag operation. /// public static void SetDragSourceIgnore(UIElement source, bool value) { @@ -522,7 +525,7 @@ public static void SetDragSourceIgnore(UIElement source, bool value) } /// - /// Gets or Sets wheter the drag action should be started only directly on a selected item + /// Gets or sets whether the drag action should be started only directly on a selected item. /// or also on the free control space (e.g. in a ListBox). /// public static readonly DependencyProperty DragDirectlySelectedOnlyProperty @@ -576,7 +579,7 @@ public static void SetDragDropCopyKeyState(UIElement target, DragDropKeyStates v } /// - /// Gets or Sets whether if the default DragAdorner should be use. + /// Gets or sets whether if the default DragAdorner should be use. /// public static readonly DependencyProperty UseDefaultDragAdornerProperty = DependencyProperty.RegisterAttached("UseDefaultDragAdorner", @@ -601,7 +604,7 @@ public static void SetUseDefaultDragAdorner(UIElement target, bool value) } /// - /// Gets or Sets the opacity of the default DragAdorner. + /// Gets or sets the opacity of the default DragAdorner. /// public static readonly DependencyProperty DefaultDragAdornerOpacityProperty = DependencyProperty.RegisterAttached("DefaultDragAdornerOpacity", @@ -626,7 +629,7 @@ public static void SetDefaultDragAdornerOpacity(UIElement target, double value) } /// - /// Gets or Sets the horizontal and vertical proportion at which the pointer will anchor on the DragAdorner. + /// Gets or sets the horizontal and vertical proportion at which the pointer will anchor on the DragAdorner. /// public static readonly DependencyProperty DragMouseAnchorPointProperty = DependencyProperty.RegisterAttached("DragMouseAnchorPoint", @@ -651,7 +654,7 @@ public static void SetDragMouseAnchorPoint(UIElement target, Point value) } /// - /// Gets or Sets the translation transform which will be used for the DragAdorner. + /// Gets or sets the translation transform which will be used for the DragAdorner. /// public static readonly DependencyProperty DragAdornerTranslationProperty = DependencyProperty.RegisterAttached("DragAdornerTranslation", @@ -676,7 +679,7 @@ public static void SetDragAdornerTranslation(UIElement element, Point value) } /// - /// Gets or Sets the translation transform which will be used for the EffectAdorner. + /// Gets or sets the translation transform which will be used for the EffectAdorner. /// public static readonly DependencyProperty EffectAdornerTranslationProperty = DependencyProperty.RegisterAttached("EffectAdornerTranslation", @@ -701,7 +704,7 @@ public static void SetEffectAdornerTranslation(UIElement element, Point value) } /// - /// Gets or Sets a DataTemplate for the DragAdorner. + /// Gets or sets a DataTemplate for the DragAdorner. /// public static readonly DependencyProperty DragAdornerTemplateProperty = DependencyProperty.RegisterAttached("DragAdornerTemplate", @@ -725,7 +728,7 @@ public static void SetDragAdornerTemplate(UIElement target, DataTemplate value) } /// - /// Gets or Sets a DataTemplate for the DragAdorner based on the DropTarget. + /// Gets or sets a DataTemplate for the DragAdorner based on the DropTarget. /// public static readonly DependencyProperty DropAdornerTemplateProperty = DependencyProperty.RegisterAttached("DropAdornerTemplate", @@ -749,7 +752,7 @@ public static void SetDropAdornerTemplate(UIElement target, DataTemplate value) } /// - /// Gets or Sets a DataTemplateSelector for the DragAdorner. + /// Gets or sets a DataTemplateSelector for the DragAdorner. /// public static readonly DependencyProperty DragAdornerTemplateSelectorProperty = DependencyProperty.RegisterAttached("DragAdornerTemplateSelector", @@ -774,7 +777,7 @@ public static DataTemplateSelector GetDragAdornerTemplateSelector(DependencyObje } /// - /// Gets or Sets a DataTemplateSelector for the DragAdorner based on the DropTarget. + /// Gets or sets a DataTemplateSelector for the DragAdorner based on the DropTarget. /// public static readonly DependencyProperty DropAdornerTemplateSelectorProperty = DependencyProperty.RegisterAttached("DropAdornerTemplateSelector", @@ -824,7 +827,7 @@ public static void SetUseVisualSourceItemSizeForDragAdorner(UIElement target, bo } /// - /// Gets or Sets whether if the default DataTemplate for the effects should be use. + /// Gets or sets whether if the default DataTemplate for the effects should be use. /// public static readonly DependencyProperty UseDefaultEffectDataTemplateProperty = DependencyProperty.RegisterAttached("UseDefaultEffectDataTemplate", @@ -849,7 +852,7 @@ public static void SetUseDefaultEffectDataTemplate(UIElement target, bool value) } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type None. + /// Gets or sets a EffectAdorner DataTemplate for effect type None. /// public static readonly DependencyProperty EffectNoneAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectNoneAdornerTemplate", @@ -874,7 +877,7 @@ public static void SetEffectNoneAdornerTemplate(UIElement target, DataTemplate v } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type Copy. + /// Gets or sets a EffectAdorner DataTemplate for effect type Copy. /// public static readonly DependencyProperty EffectCopyAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectCopyAdornerTemplate", @@ -899,7 +902,7 @@ public static void SetEffectCopyAdornerTemplate(UIElement target, DataTemplate v } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type Move. + /// Gets or sets a EffectAdorner DataTemplate for effect type Move. /// public static readonly DependencyProperty EffectMoveAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectMoveAdornerTemplate", @@ -924,7 +927,7 @@ public static void SetEffectMoveAdornerTemplate(UIElement target, DataTemplate v } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type Link. + /// Gets or sets a EffectAdorner DataTemplate for effect type Link. /// public static readonly DependencyProperty EffectLinkAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectLinkAdornerTemplate", @@ -949,7 +952,7 @@ public static void SetEffectLinkAdornerTemplate(UIElement target, DataTemplate v } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type All. + /// Gets or sets a EffectAdorner DataTemplate for effect type All. /// public static readonly DependencyProperty EffectAllAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectAllAdornerTemplate", @@ -974,7 +977,7 @@ public static void SetEffectAllAdornerTemplate(UIElement target, DataTemplate va } /// - /// Gets or Sets a EffectAdorner DataTemplate for effect type Scroll. + /// Gets or sets a EffectAdorner DataTemplate for effect type Scroll. /// public static readonly DependencyProperty EffectScrollAdornerTemplateProperty = DependencyProperty.RegisterAttached("EffectScrollAdornerTemplate", @@ -999,9 +1002,9 @@ public static void SetEffectScrollAdornerTemplate(UIElement target, DataTemplate } /// - /// Gets or Sets the Orientation which should be used for the drag drop action (default null). - /// 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. + /// Gets or sets the Orientation which should be used for the drag drop action (default null). + /// Normally it will be look up to find the correct orientation of the inner ItemsPanel, + /// but sometimes it's necessary to force the orientation, if the look up is wrong. /// public static readonly DependencyProperty ItemsPanelOrientationProperty = DependencyProperty.RegisterAttached("ItemsPanelOrientation", @@ -1011,8 +1014,8 @@ public static readonly DependencyProperty ItemsPanelOrientationProperty /// /// Gets the Orientation which should be used for the drag drop action (default null). - /// 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. + /// Normally it will be look up to find the correct orientation of the inner ItemsPanel, + /// but sometimes it's necessary to force the orientation, if the look up is wrong. /// public static Orientation? GetItemsPanelOrientation(UIElement source) { @@ -1021,8 +1024,8 @@ public static readonly DependencyProperty ItemsPanelOrientationProperty /// /// Sets the Orientation which should be used for the drag drop action (default null). - /// 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. + /// Normally it will be look up to find the correct orientation of the inner ItemsPanel, + /// but sometimes it's necessary to force the orientation, if the look up is wrong. /// public static void SetItemsPanelOrientation(UIElement source, Orientation? value) { From 3b5ed0b626add0c73e09a5c6a340f4c704db0633 Mon Sep 17 00:00:00 2001 From: punker76 Date: Thu, 26 Aug 2021 15:09:39 +0200 Subject: [PATCH 3/7] (#385) Fix wrong position for Drag Adorner on High DPI screens --- src/GongSolutions.WPF.DragDrop/DragDrop.cs | 9 +- .../DragDropPreview.cs | 18 ++-- .../Utilities/CursorHelper.cs | 50 ++++++++++ .../Utilities/MouseHelper.cs | 92 +++++++++++++++++++ ...{NativeMethods.cs => WindowStyleHelper.cs} | 86 +---------------- 5 files changed, 159 insertions(+), 96 deletions(-) create mode 100644 src/GongSolutions.WPF.DragDrop/Utilities/CursorHelper.cs create mode 100644 src/GongSolutions.WPF.DragDrop/Utilities/MouseHelper.cs rename src/GongSolutions.WPF.DragDrop/Utilities/{NativeMethods.cs => WindowStyleHelper.cs} (57%) diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.cs index 42dd7d1f..e3b494cf 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.cs @@ -560,10 +560,11 @@ private static void DoDragSourceMove(object sender, Point position) try { _dragInProgress = true; - hookId = NativeMethods.HookMouseMove(point => + + hookId = MouseHelper.HookMouseMove(point => { - DragDropPreview?.Move(DragDropPreview.PlacementTarget.PointFromScreen(point)); - DragDropEffectPreview?.Move(DragDropEffectPreview.PlacementTarget.PointFromScreen(point)); + DragDropPreview?.Move(CursorHelper.GetCurrentCursorPosition(DragDropPreview.PlacementTarget, point)); + DragDropEffectPreview?.Move(CursorHelper.GetCurrentCursorPosition(DragDropEffectPreview.PlacementTarget, point)); }); var dragDropHandler = dragInfo.DragDropHandler ?? System.Windows.DragDrop.DoDragDrop; @@ -584,7 +585,7 @@ private static void DoDragSourceMove(object sender, Point position) } finally { - NativeMethods.RemoveHook(hookId); + MouseHelper.RemoveHook(hookId); _dragInProgress = false; _dragInfo = null; } diff --git a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs index 7160d738..48655aa9 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Interop; @@ -11,10 +11,9 @@ internal class DragDropPreview : Popup public DragDropPreview(UIElement rootElement, UIElement previewElement, Point translation, Point anchorPoint) { this.PlacementTarget = rootElement; + this.Placement = PlacementMode.Relative; this.AllowsTransparency = true; this.Focusable = false; - this.Placement = PlacementMode.Relative; - this.PlacementTarget = rootElement; this.PopupAnimation = PopupAnimation.Fade; this.StaysOpen = true; this.HorizontalOffset = -9999; @@ -33,8 +32,9 @@ public DragDropPreview(UIElement rootElement, UIElement previewElement, Point tr internal void Move(Point point) { - var translationX = point.X + this.Translation.X; - var translationY = point.Y + this.Translation.Y; + var translation = this.Translation; + var translationX = point.X + translation.X; + var translationY = point.Y + translation.Y; var renderSize = this.Child.RenderSize; @@ -59,12 +59,12 @@ protected override void OnOpened(EventArgs e) if (PresentationSource.FromVisual(this.Child) is HwndSource hwndSource) { var windowHandle = hwndSource.Handle; - var wsex = NativeMethods.GetWindowStyleEx(windowHandle); + var wsex = WindowStyleHelper.GetWindowStyleEx(windowHandle); - wsex |= NativeMethods.WS_EX.NOACTIVATE; // We don't want our this window to be activated - wsex |= NativeMethods.WS_EX.TRANSPARENT; + wsex |= WindowStyleHelper.WS_EX.NOACTIVATE; // We don't want our this window to be activated + wsex |= WindowStyleHelper.WS_EX.TRANSPARENT; - NativeMethods.SetWindowStyleEx(windowHandle, wsex); + WindowStyleHelper.SetWindowStyleEx(windowHandle, wsex); } } } diff --git a/src/GongSolutions.WPF.DragDrop/Utilities/CursorHelper.cs b/src/GongSolutions.WPF.DragDrop/Utilities/CursorHelper.cs new file mode 100644 index 00000000..a68d9ec5 --- /dev/null +++ b/src/GongSolutions.WPF.DragDrop/Utilities/CursorHelper.cs @@ -0,0 +1,50 @@ +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Media; + +namespace GongSolutions.Wpf.DragDrop.Utilities +{ + internal static class CursorHelper + { + [StructLayout(LayoutKind.Sequential)] + private struct POINT + { + public int x; + public int y; + }; + + [DllImport("user32.dll", EntryPoint = "GetCursorPos", ExactSpelling = true, CharSet = CharSet.Auto)] + private static extern bool TryGetCursorPos(ref POINT pt); + + public static Point GetCurrentCursorPosition(Visual relativeTo, Point fallBack) + { + var pt = new POINT(); + + bool returnValue; + + try + { + returnValue = TryGetCursorPos(ref pt); + } + catch + { + returnValue = false; + } + + // Sometimes Win32 will fail this call, such as if you are + // not running in the interactive desktop. For example, + // a secure screen saver may be running. + if (returnValue == false) + { + System.Diagnostics.Debug.WriteLine("GetCursorPos failed!"); + + // pt.x = 0; + // pt.y = 0; + + return relativeTo.PointFromScreen(fallBack); + } + + return relativeTo.PointFromScreen(new Point(pt.x, pt.y)); + } + } +} \ No newline at end of file diff --git a/src/GongSolutions.WPF.DragDrop/Utilities/MouseHelper.cs b/src/GongSolutions.WPF.DragDrop/Utilities/MouseHelper.cs new file mode 100644 index 00000000..b1b35357 --- /dev/null +++ b/src/GongSolutions.WPF.DragDrop/Utilities/MouseHelper.cs @@ -0,0 +1,92 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace GongSolutions.Wpf.DragDrop.Utilities +{ + // Taken from Fluent Drag&Drop https://github.com/punker76/FluentDragDrop + internal static class MouseHelper + { + private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); + + private const int WH_MOUSE_LL = 14; + + private static IntPtr _hookID = IntPtr.Zero; + private static Action _mouseMoveHandler; + + // wee need to keep the variable to prevent the GarbageCollector to remove the HookCallback + // https://social.msdn.microsoft.com/Forums/vstudio/en-US/68fdc3dc-8d77-48c4-875c-5312baa56aee/how-to-fix-callbackoncollecteddelegate-exception?forum=netfxbcl + private static LowLevelMouseProc _proc = HookCallback; + + internal static IntPtr HookMouseMove(Action mouseMoveHandler) + { + _mouseMoveHandler = mouseMoveHandler; + + using (var process = Process.GetCurrentProcess()) + { + using (var module = process.MainModule) + { + return SetWindowsHookEx(WH_MOUSE_LL, _proc, GetModuleHandle(module.ModuleName), 0); + } + } + } + + internal static void RemoveHook(IntPtr hookId) + { + UnhookWindowsHookEx(hookId); + } + + private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) + { + if (nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam) + { + MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); + //Debug.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y); + var mousePosScreen = new System.Windows.Point(hookStruct.pt.x, hookStruct.pt.y); + _mouseMoveHandler?.Invoke(mousePosScreen); + } + + return CallNextHookEx(_hookID, nCode, wParam, lParam); + } + + private enum MouseMessages + { + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_MOUSEMOVE = 0x0200, + WM_MOUSEWHEEL = 0x020A, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205 + } + + [StructLayout(LayoutKind.Sequential)] + private struct POINT + { + public int x; + public int y; + } + + [StructLayout(LayoutKind.Sequential)] + private struct MSLLHOOKSTRUCT + { + public POINT pt; + public uint mouseData; + public uint flags; + public uint time; + public IntPtr dwExtraInfo; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UnhookWindowsHookEx(IntPtr hhk); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr GetModuleHandle(string lpModuleName); + } +} \ No newline at end of file diff --git a/src/GongSolutions.WPF.DragDrop/Utilities/NativeMethods.cs b/src/GongSolutions.WPF.DragDrop/Utilities/WindowStyleHelper.cs similarity index 57% rename from src/GongSolutions.WPF.DragDrop/Utilities/NativeMethods.cs rename to src/GongSolutions.WPF.DragDrop/Utilities/WindowStyleHelper.cs index 0a17ba43..685e62c1 100644 --- a/src/GongSolutions.WPF.DragDrop/Utilities/NativeMethods.cs +++ b/src/GongSolutions.WPF.DragDrop/Utilities/WindowStyleHelper.cs @@ -1,93 +1,13 @@ using System; using System.ComponentModel; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// Mouse move hook inspired by Fluent Drag&Drop https://github.com/punker76/FluentDragDrop namespace GongSolutions.Wpf.DragDrop.Utilities { - internal static class NativeMethods + internal static class WindowStyleHelper { - private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); - - private const int WH_MOUSE_LL = 14; - - private static IntPtr _hookID = IntPtr.Zero; - private static Action _mouseMoveHandler; - - // wee need to keep the variable to prevent the GarbageCollector to remove the HookCallback - // https://social.msdn.microsoft.com/Forums/vstudio/en-US/68fdc3dc-8d77-48c4-875c-5312baa56aee/how-to-fix-callbackoncollecteddelegate-exception?forum=netfxbcl - private static LowLevelMouseProc _proc = HookCallback; - - internal static IntPtr HookMouseMove(Action mouseMoveHandler) - { - _mouseMoveHandler = mouseMoveHandler; - - using var process = Process.GetCurrentProcess(); - using var module = process.MainModule; - return SetWindowsHookEx(WH_MOUSE_LL, _proc, GetModuleHandle(module.ModuleName), 0); - } - - internal static void RemoveHook(IntPtr hookId) - { - UnhookWindowsHookEx(hookId); - } - - private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) - { - if (nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam) - { - MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); - //Debug.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y); - var mousePosScreen = new System.Windows.Point(hookStruct.pt.x, hookStruct.pt.y); - _mouseMoveHandler?.Invoke(mousePosScreen); - } - - return CallNextHookEx(_hookID, nCode, wParam, lParam); - } - - private enum MouseMessages - { - WM_LBUTTONDOWN = 0x0201, - WM_LBUTTONUP = 0x0202, - WM_MOUSEMOVE = 0x0200, - WM_MOUSEWHEEL = 0x020A, - WM_RBUTTONDOWN = 0x0204, - WM_RBUTTONUP = 0x0205 - } - - [StructLayout(LayoutKind.Sequential)] - private struct POINT - { - public int x; - public int y; - } - - [StructLayout(LayoutKind.Sequential)] - private struct MSLLHOOKSTRUCT - { - public POINT pt; - public uint mouseData; - public uint flags; - public uint time; - public IntPtr dwExtraInfo; - } - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool UnhookWindowsHookEx(IntPtr hhk); - - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); - - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr GetModuleHandle(string lpModuleName); - [CLSCompliant(false)] internal static WS_EX GetWindowStyleEx(IntPtr hWnd) { @@ -103,7 +23,7 @@ internal static WS_EX SetWindowStyleEx(IntPtr hWnd, WS_EX dwNewLong) // This is aliased as a macro in 32bit Windows. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [MethodImpl(MethodImplOptions.NoInlining)] - internal static IntPtr GetWindowLongPtr(IntPtr hwnd, GWL nIndex) + private static IntPtr GetWindowLongPtr(IntPtr hwnd, GWL nIndex) { var ret = 8 == IntPtr.Size ? GetWindowLongPtr64(hwnd, nIndex) @@ -130,7 +50,7 @@ internal static IntPtr GetWindowLongPtr(IntPtr hwnd, GWL nIndex) // This is aliased as a macro in 32bit Windows. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [MethodImpl(MethodImplOptions.NoInlining)] - internal static IntPtr SetWindowLongPtr(IntPtr hwnd, GWL nIndex, IntPtr dwNewLong) + private static IntPtr SetWindowLongPtr(IntPtr hwnd, GWL nIndex, IntPtr dwNewLong) { if (8 == IntPtr.Size) { From e7e49bb2e4e8b28155923ec06d8e1566af0e9755 Mon Sep 17 00:00:00 2001 From: punker76 Date: Thu, 26 Aug 2021 15:37:01 +0200 Subject: [PATCH 4/7] (#387) Fix showing 2 effects on Drag drop operations --- .../DragDrop.Properties.cs | 2 ++ src/GongSolutions.WPF.DragDrop/DragDrop.cs | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs index dd80cc23..9e604428 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.Properties.cs @@ -79,6 +79,7 @@ private static void IsDragSourceChanged(DependencyObject d, DependencyPropertyCh uiElement.PreviewTouchMove += DragSourceOnTouchMove; uiElement.QueryContinueDrag += DragSourceOnQueryContinueDrag; + uiElement.GiveFeedback += DragSourceOnGiveFeedback; } else { @@ -91,6 +92,7 @@ private static void IsDragSourceChanged(DependencyObject d, DependencyPropertyCh uiElement.PreviewTouchMove -= DragSourceOnTouchMove; uiElement.QueryContinueDrag -= DragSourceOnQueryContinueDrag; + uiElement.GiveFeedback -= DragSourceOnGiveFeedback; } } diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.cs index e3b494cf..8debed4b 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.cs @@ -794,6 +794,28 @@ private static void DropTargetOnDrop(object sender, DragEventArgs e, EventType e Mouse.OverrideCursor = null; } + private static void DragSourceOnGiveFeedback(object sender, GiveFeedbackEventArgs e) + { + if (DragDropEffectPreview != 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 void DropTargetOnGiveFeedback(object sender, GiveFeedbackEventArgs e) { if (DragDropEffectPreview != null) From f6b712bea75d568779a38bcef48b67122f9e6849 Mon Sep 17 00:00:00 2001 From: punker76 Date: Tue, 7 Sep 2021 12:54:37 +0200 Subject: [PATCH 5/7] (#389) Fix for drag preview doesn't show up if the source is not a target --- src/GongSolutions.WPF.DragDrop/DragDrop.cs | 153 ++++++----- .../DragDropPreview.cs | 238 +++++++++++++++++- .../Utilities/DragDropExtensions.cs | 4 +- src/Showcase/Views/SettingsView.xaml | 3 + 4 files changed, 306 insertions(+), 92 deletions(-) diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.cs index 8debed4b..2d32ffb5 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.cs @@ -14,89 +14,64 @@ namespace GongSolutions.Wpf.DragDrop { public static partial class DragDrop { - private static DragDropPreview GetDragDropPreview(DropInfo dropInfo, UIElement target) + internal static DataTemplate TryGetDragAdornerTemplate(UIElement source, UIElement sender) { - 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) + var template = source is not null ? GetDragAdornerTemplate(source) : null; + if (template is null && sender is not null) { - template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection); + template = GetDragAdornerTemplate(sender); } - if (template != null || templateSelector != null) - { - if (dragInfo.Data is IEnumerable items && !(items is string)) - { - var itemsCount = items.Cast().Count(); - var maxItemsCount = TryGetDragPreviewMaxItemsCount(dragInfo, target); - if (!useDefaultDragAdorner && itemsCount <= maxItemsCount) - { - var itemsControl = new ItemsControl(); - - // sort items if necessary before creating the preview - var sorter = TryGetDragPreviewItemsSorter(dragInfo, target); - if (sorter != null) - { - itemsControl.ItemsSource = sorter.SortDragPreviewItems(items); - } - else - { - itemsControl.ItemsSource = items; - } + return template; + } - itemsControl.ItemTemplate = template; - itemsControl.ItemTemplateSelector = templateSelector; - itemsControl.Tag = dragInfo; + internal static DataTemplateSelector TryGetDragAdornerTemplateSelector(UIElement source, UIElement sender) + { + var templateSelector = source is not null ? GetDragAdornerTemplateSelector(source) : null; + if (templateSelector is null && sender is not null) + { + templateSelector = GetDragAdornerTemplateSelector(sender); + } - if (useVisualSourceItemSizeForDragAdorner) - { - var bounds = VisualTreeExtensions.GetVisibleDescendantBounds(dragInfo.VisualSourceItem); - itemsControl.SetValue(FrameworkElement.MinWidthProperty, bounds.Width); - } + return templateSelector; + } - // 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; + internal static DataTemplate TryGetDropAdornerTemplate(UIElement source, UIElement sender) + { + var template = source is not null ? GetDropAdornerTemplate(source) : null; + if (template is null && sender is not null) + { + template = GetDropAdornerTemplate(sender); + } - if (useVisualSourceItemSizeForDragAdorner) - { - var bounds = VisualTreeExtensions.GetVisibleDescendantBounds(dragInfo.VisualSourceItem); - contentPresenter.SetValue(FrameworkElement.MinWidthProperty, bounds.Width); - contentPresenter.SetValue(FrameworkElement.MinHeightProperty, bounds.Height); - } + return template; + } - adornment = contentPresenter; - } + internal static DataTemplateSelector TryGetDropAdornerTemplateSelector(UIElement source, UIElement sender) + { + var templateSelector = source is not null ? GetDropAdornerTemplateSelector(source) : null; + if (templateSelector is null && sender is not null) + { + templateSelector = GetDropAdornerTemplateSelector(sender); } - if (adornment != null) + return templateSelector; + } + + private static DragDropPreview GetDragDropPreview(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + var visualSource = dragInfo?.VisualSource; + if (visualSource is null) { - if (useDefaultDragAdorner) - { - adornment.Opacity = GetDefaultDragAdornerOpacity(dragInfo.VisualSource); - } + return null; + } - var rootElement = TryGetRootElementFinder(target).FindRoot(dropInfo.VisualTarget ?? dragInfo.VisualSource); + var rootElement = TryGetRootElementFinder(sender).FindRoot(visualTarget ?? visualSource); - var preview = new DragDropPreview(rootElement, adornment, GetDragAdornerTranslation(dragInfo.VisualSource), GetDragMouseAnchorPoint(dragInfo.VisualSource)) { IsOpen = true }; + var preview = new DragDropPreview(rootElement, dragInfo, visualTarget ?? visualSource, sender); + if (preview.Child != null) + { + preview.IsOpen = true; return preview; } @@ -106,7 +81,7 @@ private static DragDropPreview GetDragDropPreview(DropInfo dropInfo, UIElement t private static DragDropEffectPreview GetDragDropEffectPreview(DropInfo dropInfo, UIElement sender) { var dragInfo = dropInfo.DragInfo; - var template = GetDragDropEffecTemplate(dragInfo.VisualSource, dropInfo); + var template = GetDragDropEffectTemplate(dragInfo.VisualSource, dropInfo); if (template != null) { @@ -114,14 +89,18 @@ private static DragDropEffectPreview GetDragDropEffectPreview(DropInfo dropInfo, var adornment = new ContentPresenter { Content = dragInfo.Data, ContentTemplate = template }; - var preview = new DragDropEffectPreview(rootElement, adornment, GetEffectAdornerTranslation(dragInfo.VisualSource), dropInfo.Effects, dropInfo.EffectText, dropInfo.DestinationText) { IsOpen = true }; + var preview = new DragDropEffectPreview(rootElement, adornment, GetEffectAdornerTranslation(dragInfo.VisualSource), dropInfo.Effects, dropInfo.EffectText, dropInfo.DestinationText) + { + IsOpen = true + }; + return preview; } return null; } - private static DataTemplate GetDragDropEffecTemplate(UIElement target, DropInfo dropInfo) + private static DataTemplate GetDragDropEffectTemplate(UIElement target, DropInfo dropInfo) { if (target is null) { @@ -303,7 +282,7 @@ private static IRootElementFinder TryGetRootElementFinder(UIElement sender) return rootElementFinder ?? DefaultRootElementFinder; } - private static int TryGetDragPreviewMaxItemsCount(IDragInfo dragInfo, UIElement sender) + internal static int TryGetDragPreviewMaxItemsCount(IDragInfo dragInfo, UIElement sender) { var itemsCount = dragInfo?.VisualSource != null ? GetDragPreviewMaxItemsCount(dragInfo.VisualSource) : -1; if (itemsCount < 0 && sender != null) @@ -314,7 +293,7 @@ private static int TryGetDragPreviewMaxItemsCount(IDragInfo dragInfo, UIElement return itemsCount < 0 || itemsCount >= int.MaxValue ? 10 : itemsCount; } - private static IDragPreviewItemsSorter TryGetDragPreviewItemsSorter(IDragInfo dragInfo, UIElement sender) + internal static IDragPreviewItemsSorter TryGetDragPreviewItemsSorter(IDragInfo dragInfo, UIElement sender) { var itemsSorter = dragInfo?.VisualSource != null ? GetDragPreviewItemsSorter(dragInfo.VisualSource) : null; if (itemsSorter is null && sender != null) @@ -490,7 +469,7 @@ private static void DragSourceOnTouchMove(object sender, TouchEventArgs e) return; } - DoDragSourceMove(sender, e.GetTouchPoint((IInputElement)sender).Position); + DoDragSourceMove(sender, element => e.GetTouchPoint(element).Position); } } @@ -512,11 +491,11 @@ private static void DragSourceOnMouseMove(object sender, MouseEventArgs e) return; } - DoDragSourceMove(sender, e.GetPosition((IInputElement)sender)); + DoDragSourceMove(sender, element => e.GetPosition(element)); } } - private static void DoDragSourceMove(object sender, Point position) + private static void DoDragSourceMove(object sender, Func getPosition) { var dragInfo = _dragInfo; if (dragInfo != null && !_dragInProgress) @@ -528,6 +507,7 @@ private static void DoDragSourceMove(object sender, Point position) dragInfo.VisualSource?.ReleaseMouseCapture(); // only if the sender is the source control and the mouse point differs from an offset + var position = getPosition((IInputElement)sender); if (dragInfo.VisualSource == sender && (Math.Abs(position.X - dragStart.X) > DragDrop.GetMinimumHorizontalDragDistance(dragInfo.VisualSource) || Math.Abs(position.Y - dragStart.Y) > DragDrop.GetMinimumVerticalDragDistance(dragInfo.VisualSource))) @@ -561,6 +541,12 @@ private static void DoDragSourceMove(object sender, Point position) { _dragInProgress = true; + if (DragDropPreview is null) + { + DragDropPreview = GetDragDropPreview(dragInfo, null, sender as UIElement); + DragDropPreview?.Move(getPosition(DragDropPreview.PlacementTarget)); + } + hookId = MouseHelper.HookMouseMove(point => { DragDropPreview?.Move(CursorHelper.GetCurrentCursorPosition(DragDropPreview.PlacementTarget, point)); @@ -659,10 +645,15 @@ private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventTy dropHandler.DragOver(dropInfo); - if (DragDropPreview is null && dragInfo is { }) + if (dragInfo is not null) { - DragDropPreview = GetDragDropPreview(dropInfo, sender as UIElement); - DragDropPreview?.Move(e.GetPosition(DragDropPreview.PlacementTarget)); + if (DragDropPreview is null) + { + DragDropPreview = GetDragDropPreview(dragInfo, dropInfo.VisualTarget, sender as UIElement); + DragDropPreview?.Move(e.GetPosition(DragDropPreview.PlacementTarget)); + } + + DragDropPreview?.UpdatePreviewPresenter(dragInfo, dropInfo.VisualTarget, sender as UIElement); } Scroll(dropInfo, e); @@ -736,7 +727,7 @@ private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventTy DragDropEffectPreview.EffectText = dropInfo.EffectText; DragDropEffectPreview.DestinationText = dropInfo.DestinationText; - var template = GetDragDropEffecTemplate(dragInfo.VisualSource, dropInfo); + var template = GetDragDropEffectTemplate(dragInfo.VisualSource, dropInfo); if (template is null) { DragDropEffectPreview = null; diff --git a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs index 48655aa9..b5639ca5 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs @@ -1,7 +1,12 @@ -using System; +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 System.Windows.Interop; +using System.Windows.Media; using GongSolutions.Wpf.DragDrop.Utilities; namespace GongSolutions.Wpf.DragDrop @@ -26,25 +31,89 @@ public DragDropPreview(UIElement rootElement, UIElement previewElement, Point tr this.AnchorPoint = anchorPoint; } + public DragDropPreview(UIElement rootElement, IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + this.PlacementTarget = rootElement; + this.Placement = PlacementMode.Relative; + this.AllowsTransparency = true; + this.Focusable = false; + this.PopupAnimation = PopupAnimation.Fade; + this.StaysOpen = true; + this.HorizontalOffset = -9999; + this.VerticalOffset = -9999; + this.IsHitTestVisible = false; + this.AllowDrop = false; + this.HorizontalAlignment = HorizontalAlignment.Left; + this.VerticalAlignment = VerticalAlignment.Top; + + this.DragInfo = dragInfo; + this.Child = this.CreatePreviewPresenter(dragInfo, visualTarget, sender); + this.Translation = DragDrop.GetDragAdornerTranslation(dragInfo.VisualSource); + this.AnchorPoint = DragDrop.GetDragMouseAnchorPoint(dragInfo.VisualSource); + } + + private IDragInfo DragInfo { get; } + + private Rect VisualSourceItemBounds { get; set; } = Rect.Empty; + public Point Translation { get; } public Point AnchorPoint { get; } - internal void Move(Point point) + public bool UseDefaultDragAdorner { get; private set; } + + public static readonly DependencyProperty ItemTemplateProperty + = DependencyProperty.Register(nameof(ItemTemplate), + typeof(DataTemplate), + typeof(DragDropPreview), + new FrameworkPropertyMetadata((DataTemplate)null)); + + public DataTemplate ItemTemplate + { + get => (DataTemplate)this.GetValue(ItemTemplateProperty); + set => this.SetValue(ItemTemplateProperty, value); + } + + public static readonly DependencyProperty ItemTemplateSelectorProperty + = DependencyProperty.Register(nameof(ItemTemplateSelector), + typeof(DataTemplateSelector), + typeof(DragDropPreview), + new FrameworkPropertyMetadata((DataTemplateSelector)null)); + + public DataTemplateSelector ItemTemplateSelector + { + get => (DataTemplateSelector)this.GetValue(ItemTemplateSelectorProperty); + set => this.SetValue(ItemTemplateSelectorProperty, value); + } + + public void Move(Point point) { var translation = this.Translation; var translationX = point.X + translation.X; var translationY = point.Y + translation.Y; - var renderSize = this.Child.RenderSize; - - if (renderSize.Width > 0 && renderSize.Height > 0) + if (this.Child is not null) { - var offsetX = renderSize.Width * -this.AnchorPoint.X; - var offsetY = renderSize.Height * -this.AnchorPoint.Y; + var renderSize = this.Child.RenderSize; + + var renderSizeWidth = renderSize.Width; + var renderSizeHeight = renderSize.Height; + + // Only set if the template contains a Canvas. + if (!this.VisualSourceItemBounds.IsEmpty) + { + renderSizeWidth = Math.Min(renderSizeWidth, this.VisualSourceItemBounds.Width); + renderSizeHeight = Math.Min(renderSizeHeight, this.VisualSourceItemBounds.Height); + } - translationX += offsetX; - translationY += offsetY; + if (renderSizeWidth > 0 && renderSizeHeight > 0) + { + var offsetX = renderSizeWidth * -this.AnchorPoint.X; + var offsetY = renderSizeHeight * -this.AnchorPoint.Y; + + translationX += offsetX; + translationY += offsetY; + } } this.SetCurrentValue(HorizontalOffsetProperty, translationX); @@ -67,5 +136,156 @@ protected override void OnOpened(EventArgs e) WindowStyleHelper.SetWindowStyleEx(windowHandle, wsex); } } + + public void UpdatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + if (this.UseDefaultDragAdorner) + { + return; + } + + var visualSource = dragInfo.VisualSource; + + DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplate(visualSource, sender); + DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); + + if (template is not null) + { + templateSelector = null; + } + + this.SetCurrentValue(ItemTemplateProperty, template); + this.SetCurrentValue(ItemTemplateSelectorProperty, templateSelector); + } + + public UIElement CreatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + var visualSource = dragInfo.VisualSource; + + DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplate(visualSource, sender); + DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); + + var useDefaultDragAdorner = template is null && templateSelector is null && DragDrop.GetUseDefaultDragAdorner(visualSource); + var useVisualSourceItemSizeForDragAdorner = dragInfo.VisualSourceItem != null && DragDrop.GetUseVisualSourceItemSizeForDragAdorner(visualSource); + + if (useDefaultDragAdorner) + { + template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection); + useDefaultDragAdorner = template is not null; + } + + if (template is not null) + { + templateSelector = null; + } + + this.SetCurrentValue(ItemTemplateProperty, template); + this.SetCurrentValue(ItemTemplateSelectorProperty, templateSelector); + + this.UseDefaultDragAdorner = useDefaultDragAdorner; + + UIElement adornment = null; + + if (template != null || templateSelector != null) + { + if (dragInfo.Data is IEnumerable items && !(items is string)) + { + var itemsCount = items.Cast().Count(); + var maxItemsCount = DragDrop.TryGetDragPreviewMaxItemsCount(dragInfo, sender); + if (!useDefaultDragAdorner && itemsCount <= maxItemsCount) + { + // sort items if necessary before creating the preview + var sorter = DragDrop.TryGetDragPreviewItemsSorter(dragInfo, sender); + + var itemsControl = new ItemsControl + { + ItemsSource = sorter?.SortDragPreviewItems(items) ?? items, + Tag = dragInfo + }; + + itemsControl.SetBinding(ItemsControl.ItemTemplateProperty, new Binding(nameof(this.ItemTemplate)) { Source = this }); + itemsControl.SetBinding(ItemsControl.ItemTemplateSelectorProperty, new Binding(nameof(this.ItemTemplateSelector)) { Source = this }); + + if (useVisualSourceItemSizeForDragAdorner) + { + var bounds = VisualTreeExtensions.GetVisibleDescendantBounds(dragInfo.VisualSourceItem); + itemsControl.SetCurrentValue(MinWidthProperty, bounds.Width); + } + + // The ItemsControl doesn't display unless we create a grid to contain it. + var grid = new Grid(); + grid.Children.Add(itemsControl); + adornment = grid; + } + } + else + { + var contentPresenter = new ContentPresenter + { + Content = dragInfo.Data, + Tag = dragInfo + }; + + contentPresenter.SetBinding(ContentPresenter.ContentTemplateProperty, new Binding(nameof(this.ItemTemplate)) { Source = this }); + contentPresenter.SetBinding(ContentPresenter.ContentTemplateSelectorProperty, new Binding(nameof(this.ItemTemplateSelector)) { Source = this }); + + if (useVisualSourceItemSizeForDragAdorner) + { + var bounds = VisualTreeExtensions.GetVisibleDescendantBounds(dragInfo.VisualSourceItem); + contentPresenter.SetCurrentValue(MinWidthProperty, bounds.Width); + contentPresenter.SetCurrentValue(MinHeightProperty, bounds.Height); + } + + contentPresenter.Loaded += this.ContentPresenter_OnLoaded; + + adornment = contentPresenter; + } + } + + if (adornment != null && useDefaultDragAdorner) + { + adornment.Opacity = DragDrop.GetDefaultDragAdornerOpacity(visualSource); + } + + return adornment; + } + + private void ContentPresenter_OnLoaded(object sender, RoutedEventArgs e) + { + if (sender is ContentPresenter contentPresenter) + { + contentPresenter.Loaded -= this.ContentPresenter_OnLoaded; + + // If the template contains a Canvas then we get a strange size. + if (this.UseDefaultDragAdorner && this.DragInfo?.VisualSourceItem.GetVisualDescendent() is not null) + { + this.VisualSourceItemBounds = this.DragInfo?.VisualSourceItem != null ? VisualTreeHelper.GetDescendantBounds(this.DragInfo.VisualSourceItem) : Rect.Empty; + + contentPresenter.SetCurrentValue(MaxWidthProperty, this.VisualSourceItemBounds.Width); + contentPresenter.SetCurrentValue(MaxHeightProperty, this.VisualSourceItemBounds.Height); + this.SetCurrentValue(MaxWidthProperty, this.VisualSourceItemBounds.Width); + this.SetCurrentValue(MaxHeightProperty, this.VisualSourceItemBounds.Height); + } + else + { + contentPresenter.ApplyTemplate(); + if (contentPresenter.GetVisualDescendent() is not null) + { + // Get the first element and set it's vertical alignment to top. + if (contentPresenter.GetVisualDescendent() is FrameworkElement fe) + { + fe.SetCurrentValue(VerticalAlignmentProperty, VerticalAlignment.Top); + } + + this.VisualSourceItemBounds = this.DragInfo?.VisualSourceItem != null ? VisualTreeHelper.GetDescendantBounds(this.DragInfo.VisualSourceItem) : Rect.Empty; + + contentPresenter.SetCurrentValue(MaxWidthProperty, this.VisualSourceItemBounds.Width); + contentPresenter.SetCurrentValue(MaxHeightProperty, this.VisualSourceItemBounds.Height); + this.SetCurrentValue(MaxWidthProperty, this.VisualSourceItemBounds.Width); + this.SetCurrentValue(MaxHeightProperty, this.VisualSourceItemBounds.Height); + } + } + } + } } } \ No newline at end of file diff --git a/src/GongSolutions.WPF.DragDrop/Utilities/DragDropExtensions.cs b/src/GongSolutions.WPF.DragDrop/Utilities/DragDropExtensions.cs index b6d0d518..4aacbcef 100644 --- a/src/GongSolutions.WPF.DragDrop/Utilities/DragDropExtensions.cs +++ b/src/GongSolutions.WPF.DragDrop/Utilities/DragDropExtensions.cs @@ -120,8 +120,8 @@ private static BitmapSource CaptureScreen(Visual target, FlowDirection flowDirec { var vb = new VisualBrush(target); - vb.ViewportUnits = BrushMappingMode.Absolute; - vb.Viewport = bounds; + // vb.ViewportUnits = BrushMappingMode.Absolute; + // vb.Viewport = bounds; if (flowDirection == FlowDirection.RightToLeft) { diff --git a/src/Showcase/Views/SettingsView.xaml b/src/Showcase/Views/SettingsView.xaml index bc15abc3..eb5ece56 100644 --- a/src/Showcase/Views/SettingsView.xaml +++ b/src/Showcase/Views/SettingsView.xaml @@ -32,6 +32,9 @@ + From 330ec21a44d43140d000e99a5e8a9390a2934667 Mon Sep 17 00:00:00 2001 From: punker76 Date: Tue, 7 Sep 2021 15:55:28 +0200 Subject: [PATCH 6/7] (#327) Fix using correct template or template selector for source and target --- src/GongSolutions.WPF.DragDrop/DragDrop.cs | 16 ++-- .../DragDropPreview.cs | 93 ++++++++++++++----- src/Showcase/Views/MixedSamples.xaml | 2 +- 3 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/GongSolutions.WPF.DragDrop/DragDrop.cs b/src/GongSolutions.WPF.DragDrop/DragDrop.cs index 2d32ffb5..abe1413b 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDrop.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDrop.cs @@ -66,13 +66,17 @@ private static DragDropPreview GetDragDropPreview(IDragInfo dragInfo, UIElement return null; } - var rootElement = TryGetRootElementFinder(sender).FindRoot(visualTarget ?? visualSource); - - var preview = new DragDropPreview(rootElement, dragInfo, visualTarget ?? visualSource, sender); - if (preview.Child != null) + var hasDragDropPreview = DragDropPreview.HasDragDropPreview(dragInfo, visualTarget ?? visualSource, sender); + if (hasDragDropPreview) { - preview.IsOpen = true; - return preview; + var rootElement = TryGetRootElementFinder(sender).FindRoot(visualTarget ?? visualSource); + + var preview = new DragDropPreview(rootElement, dragInfo, visualTarget ?? visualSource, sender); + if (preview.Child != null) + { + preview.IsOpen = true; + return preview; + } } return null; diff --git a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs index b5639ca5..fa8dfa9f 100644 --- a/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs +++ b/src/GongSolutions.WPF.DragDrop/DragDropPreview.cs @@ -137,62 +137,105 @@ protected override void OnOpened(EventArgs e) } } - public void UpdatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + public static bool HasDragDropPreview(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) { - if (this.UseDefaultDragAdorner) + var visualSource = dragInfo?.VisualSource; + if (visualSource is null) { - return; + return false; } - var visualSource = dragInfo.VisualSource; - - DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplate(visualSource, sender); - DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); + // Check for target template or template selector + DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender); + DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender); if (template is not null) { templateSelector = null; } - this.SetCurrentValue(ItemTemplateProperty, template); - this.SetCurrentValue(ItemTemplateSelectorProperty, templateSelector); - } + // Check for source template or template selector if there is no target one + if (template is null && templateSelector is null) + { + template = DragDrop.TryGetDragAdornerTemplate(visualSource, sender); + templateSelector = DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); - public UIElement CreatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) - { - var visualSource = dragInfo.VisualSource; + var useDefaultDragAdorner = template is null && templateSelector is null && DragDrop.GetUseDefaultDragAdorner(visualSource); + if (useDefaultDragAdorner) + { + template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection); + } - DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplate(visualSource, sender); - DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender) ?? DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); + if (template is not null) + { + templateSelector = null; + } + } - var useDefaultDragAdorner = template is null && templateSelector is null && DragDrop.GetUseDefaultDragAdorner(visualSource); - var useVisualSourceItemSizeForDragAdorner = dragInfo.VisualSourceItem != null && DragDrop.GetUseVisualSourceItemSizeForDragAdorner(visualSource); + return template is not null || templateSelector is not null; + } - if (useDefaultDragAdorner) + public void UpdatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + var visualSource = dragInfo?.VisualSource; + if (visualSource is null) { - template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection); - useDefaultDragAdorner = template is not null; + return; } + // Get target template or template selector + DataTemplate template = DragDrop.TryGetDropAdornerTemplate(visualTarget, sender); + DataTemplateSelector templateSelector = DragDrop.TryGetDropAdornerTemplateSelector(visualTarget, sender); + if (template is not null) { templateSelector = null; } - this.SetCurrentValue(ItemTemplateProperty, template); + // Get source template or template selector if there is no target one + if (template is null && templateSelector is null) + { + template = DragDrop.TryGetDragAdornerTemplate(visualSource, sender); + templateSelector = DragDrop.TryGetDragAdornerTemplateSelector(visualSource, sender); + + this.UseDefaultDragAdorner = template is null && templateSelector is null && DragDrop.GetUseDefaultDragAdorner(visualSource); + if (this.UseDefaultDragAdorner) + { + template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection); + this.UseDefaultDragAdorner = template is not null; + } + + if (template is not null) + { + templateSelector = null; + } + } + this.SetCurrentValue(ItemTemplateSelectorProperty, templateSelector); + this.SetCurrentValue(ItemTemplateProperty, template); + } + + public UIElement CreatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarget, UIElement sender) + { + var visualSource = dragInfo?.VisualSource; + if (visualSource is null) + { + return null; + } + + var useVisualSourceItemSizeForDragAdorner = dragInfo.VisualSourceItem != null && DragDrop.GetUseVisualSourceItemSizeForDragAdorner(visualSource); - this.UseDefaultDragAdorner = useDefaultDragAdorner; + this.UpdatePreviewPresenter(dragInfo, visualTarget, sender); UIElement adornment = null; - if (template != null || templateSelector != null) + if (this.ItemTemplate != null || this.ItemTemplateSelector != null) { if (dragInfo.Data is IEnumerable items && !(items is string)) { var itemsCount = items.Cast().Count(); var maxItemsCount = DragDrop.TryGetDragPreviewMaxItemsCount(dragInfo, sender); - if (!useDefaultDragAdorner && itemsCount <= maxItemsCount) + if (!this.UseDefaultDragAdorner && itemsCount <= maxItemsCount) { // sort items if necessary before creating the preview var sorter = DragDrop.TryGetDragPreviewItemsSorter(dragInfo, sender); @@ -242,7 +285,7 @@ public UIElement CreatePreviewPresenter(IDragInfo dragInfo, UIElement visualTarg } } - if (adornment != null && useDefaultDragAdorner) + if (adornment != null && this.UseDefaultDragAdorner) { adornment.Opacity = DragDrop.GetDefaultDragAdornerOpacity(visualSource); } diff --git a/src/Showcase/Views/MixedSamples.xaml b/src/Showcase/Views/MixedSamples.xaml index a1191995..95956856 100644 --- a/src/Showcase/Views/MixedSamples.xaml +++ b/src/Showcase/Views/MixedSamples.xaml @@ -296,9 +296,9 @@ Date: Tue, 7 Sep 2021 15:58:50 +0200 Subject: [PATCH 7/7] Revert changes to the mixed sample --- src/Showcase/Views/MixedSamples.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Showcase/Views/MixedSamples.xaml b/src/Showcase/Views/MixedSamples.xaml index 95956856..a1191995 100644 --- a/src/Showcase/Views/MixedSamples.xaml +++ b/src/Showcase/Views/MixedSamples.xaml @@ -296,9 +296,9 @@