Skip to content

Commit

Permalink
Add support for drag images and drop descriptions (#6576)
Browse files Browse the repository at this point in the history
* Allow drop targets to display a drag image while the cursor is over the target window and add support for drop description icons and text.

    *Narrow the scope to drop targets with support for drop icons and text.

    *Maintain the default DropTarget behavior with a last flag.

    *If the application specifies a DropIcon greather than DropIcon.Invalid (default), display the drag image, drop icon, and text specified in the DragEventArgs.

    *Add a DragDropHelper class to interface with the Shell's drag-image manager and provide methods to get and set the drop description.

    *Add a DragDrop functional test form to WinformsControlsTest with files for testing drag-and-drop operations.

* Tweak the drop description icon names.

* Cleanup feedback from RussKie

* Build fix

* Add IDragSourceHelper2 interface, SHDRAGIMAGE structure, and implement DragDropHelper.SetDragImage().

* Add the Control.DoDratgDrop API for specifying the drag image bitmap.
Add support for the drag-and-drop helper object calls to IDataObject::SetData and IDataObject::GetDataHere.

* Fix naming rule violation.

* Implement a CopyDragDropStgMedium function for copying drag-and-drop storage mediums.

* Remove unused DragDropHelper methods and add comments.

* Add CopyDragDropStgMedium format validation and comments.

* Hide the PictureBox default pointer on the test form.

* Cleanup feedback from RussKie.

* Tweak DragDropHelper return values.

* Fix the cursor from getting stuck and fix the cursor offset.

* Fix memory leak and add additional formats.

* Allow the application to specify the drag image bitmap via GivefeedbackEventArgs.

* Add more comments.

* Cleanup some feedback from welkante.
Implement change detection for drop descriptions in DragEnter and DragOver.
Make DragOver symmetrical in API with DragEnter.

* Reject SetData calls which have a non-NULL target device pointer.

* Add an internal drag and drop format class to hold related private formats and storage mediums.
Encapsulate the logic to copy the storage medium when retrieving the private formats.
Include a finalizer and call ReleaseStgMedium to free copied storage mediums marked for release.

* Handle when the caller retains ownership of the storage medium and make a copy.

* Reject incompatible storage medium types in IDataObject::SetData.

* Debug assert null target device pointer.

* Cleanup the return and let it fall through like previously.

* Add the Control.DoDragDrop API surface.
  -Optimize the drop description change detection in DropTarget.
  -Add drag image change detection in DropSource.
  -Set and get the DropTarget state to help support controls which have a complex internal structure.
  -Make DragEnter/DragOver more symmetrical.
  -Handle the DragOver edge case when a new drag image is specified in DropSource.GiveFeedback.
  -Handle the DragOver edge case when the previous DropTarget state was not DragEnter.

* Add the ToolStripItem.DoDragDrop API surface.
 -Add drag image and drop description support for ToolStrip.
 -Add a ToolStrip control to the test form for testing drag and drop operations.

* Add drag image and drop description support for RichTextBox.

* Add a RichTextBox demo to load DragAccept.rtf on DragDrop.

* Optimize drop description changes in DragLeave and Drop.

* Add a comment regarding IDataObject::GetData and when the Windows drag image manager requests formats that are not currently present in the data object.

* Improve the RichTextBox and ToolStrip demos.

* Add the InShellDragLoop format.

* Add data object support for CF_INDRAGLOOP.
 -Set the flag to true when setting the initial drag image.
 -Use the flag to check whether the data object is in a drag-and-drop loop.
 -Set the flag to false when the data has been dropped.

* Use Marshal.ReleaseComObject instead of Marshal.FinalReleaseComObject.

* Finish adding support CFSTR_INDRAGLOOP.

* Add support for dynamic addition of data during the drag loop through SetData.
 -Expand the scope of CopyDragDropStgMedium to copy all storage medium types and rename it to CopyStgMedium.

* Fix the timing of the flag.

* Clean up the comments and code.

* Free the DragDropFormat storage mediums after they are no longer in use.

* Fix the timing of the drag loop flag.

* Give the DragDropFormat appropriate method names.

* Set InDragLoop in GiveFeedback for good measure.

* Wrap the drag loop in a try finally and reset InDragLoop.

* Resolve nullable and mark members as static errors.

* Track the DropSource last target handle using IDropSourceNotify::DragEnterTarget

If a target has already been entered, call IDropTargetHelper::DragEnter to effectively display new drag images that are set during GiveFeedback.

* Cleanup feedback from dotnet api review

Align the names of DropImageType with Windows terminology including using the term Image over Icon.
 - Default -> Invalid
 - NoDropIcon -> NoImage

DragEventArgs
 - DropIcon -> DropImageType
 - Insert -> MessageReplacementToken
 - Mark the Message* properties as nullable.

GiveFeedbackEventArgs
 - Match DoDragDrop bitmap nullability and mark the bitmap non-nullable.

* Fix typo

* Check if the drop description values specified are valid

* Review feedback

* Review feedback

* Move CopyMedium to the DragDropFormat class and rename it to CopyData

* Remove unnecessary return and cleanup the demo

* Review feedback

* Update DropSource CCW to include support for IDropSourceNotify

* Review feedback

* Fix MessageReplacementToken conditional

* Be explicit about the entry indexes

* Review feedback

* Wrap the methods calls in a try/catch and return HResult of exception

* Remove the unnecessary checks in ToolStripDropTargetManager

* Simplify the checks in DropTarget

* Simplify the checks in RichTextBox.OleCallback

* Simplify the checks in DropSource

* Review feedback, first round

* Remove the unused using

* Better way of checking the incoming data object

* Have the designer generate the code

* Put the update drag image logic in its own method

* Review feedback

* Review feedback, second round

* Add the <returns> tag and describe the DragDropEffects return value

* Add GiveFeedbackEventArgs remarks

* Review feedback

* Review feedback and encapsulate the event argument equality logic

* Review feedback

* Fix duplicate symbols

* Review feedback

* Add DragDropFormat unit tests

* Supply named arguments for boolean parameters

* Cleanup the data source and variable name

* Add DragContext test data and generalize the tests

* Add DragDropHelper unit tests

* Skip test DragDropHelper_SetDragImage_ReturnsExptected

Results in Fatal error 0xC0000005 on Windows_x86 Debug and Release when executed from the command line.

* Add DragEventArgsTests for newly added properties

* Add GiveFeedbackEventArgs unit tests

* Add unit tests that verify thrown exceptions

* Add UI integration test for DragEnter

* Fix the fatal error on x86

* Tune up the test

* Unskip the skipped tests

* Fix the hard coded delay

* Fix SetDragImage tests

-Check for null drag image bits

* Review feedback

* Check for illegal cross-thread calls in DragDropHelper

-Attempts to access shell objects from a multithreaded apartment fail with error E_NOINTERFACE.

* Skip the problem tests

-These are causing other tests to fail so they are going to detention.

* Drag two items in the UI test

Co-authored-by: Jeremy Kuhne <[email protected]>
  • Loading branch information
2 people authored and dreddy-work committed Jul 7, 2022
1 parent 6491a83 commit 180639b
Show file tree
Hide file tree
Showing 51 changed files with 3,480 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ internal static class CLSID
// 00BB2763-6A77-11D0-A535-00C04FD7D062
internal static Guid AutoComplete = new Guid(0x00BB2763, 0x6A77, 0x11D0, 0xA5, 0x35, 0x00, 0xC0, 0x4F, 0xD7, 0xD0, 0x62);

// 4657278A-411B-11D2-839A-00C04FD918D0
internal static Guid DragDropHelper = new Guid(0x4657278A, 0x411B, 0x11D2, 0x83, 0x9A, 0x0, 0xC0, 0x4F, 0xD9, 0x18, 0xD0);

// C0B4E2F3-BA21-4773-8DBA-335EC946EB8B
internal static Guid FileSaveDialog = new Guid(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ internal static class IID
// 00000121-0000-0000-C000-000000000046
public static Guid IDropSource { get; } = new(0x00000121, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

// 0000012B-0000-0000-C000-000000000046
public static Guid IDropSourceNotify { get; } = new(0x0000012B, 0x000, 0x000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

// 00000122-0000-0000-C000-000000000046
public static Guid IDropTarget { get; } = new(0x00000122, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;

internal partial class Interop
{
internal static partial class Ole32
{
[ComImport]
[Guid("0000012B-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropSourceNotify
{
[PreserveSig]
HRESULT DragEnterTarget(
IntPtr hwndTarget);

[PreserveSig]
HRESULT DragLeaveTarget();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using static Interop.Kernel32;

internal static partial class Interop
{
internal static partial class Ole32
{
[DllImport(Libraries.Ole32, ExactSpelling = true)]
public static extern IntPtr OleDuplicateData(IntPtr hSrc, short cfFormat, GMEM uiFlags);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;

internal partial class Interop
{
internal static partial class Shell32
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public unsafe struct DROPDESCRIPTION
{
public DROPIMAGETYPE type;
private fixed char _szMessage[Kernel32.MAX_PATH];
private fixed char _szInsert[Kernel32.MAX_PATH];

private Span<char> szMessage
{
get { fixed (char* c = _szMessage) { return new Span<char>(c, Kernel32.MAX_PATH); } }
}

private Span<char> szInsert
{
get { fixed (char* c = _szInsert) { return new Span<char>(c, Kernel32.MAX_PATH); } }
}

public ReadOnlySpan<char> Message
{
get => szMessage.SliceAtFirstNull();
set => SpanHelpers.CopyAndTerminate(value, szMessage);
}

public ReadOnlySpan<char> Insert
{
get => szInsert.SliceAtFirstNull();
set => SpanHelpers.CopyAndTerminate(value, szInsert);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

internal partial class Interop
{
internal static partial class Shell32
{
public enum DROPIMAGETYPE
{
DROPIMAGE_INVALID = -1,
DROPIMAGE_NONE = 0,
DROPIMAGE_COPY = 1,
DROPIMAGE_MOVE = 2,
DROPIMAGE_LINK = 4,
DROPIMAGE_LABEL = 6,
DROPIMAGE_WARNING = 7,
DROPIMAGE_NOIMAGE = 8
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

internal static partial class Interop
{
internal static partial class Shell32
{
[ComImport]
[Guid("DE5BF786-477A-11D2-839D-00C04FD918D0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDragSourceHelper2
{
HRESULT InitializeFromBitmap(
in SHDRAGIMAGE pshdi,
IDataObject dataObject);

HRESULT InitializeFromWindow(
IntPtr hwnd,
in Point ppt,
IDataObject dataObject);

HRESULT SetFlags(
int dwFlags);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Drawing;
using System.Runtime.InteropServices;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;

internal static partial class Interop
{
internal static partial class Shell32
{
[ComImport]
[Guid("4657278B-411B-11D2-839A-00C04FD918D0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropTargetHelper
{
[PreserveSig]
HRESULT DragEnter(
IntPtr hwndTarget,
IComDataObject pDataObj,
ref Point ppt,
Ole32.DROPEFFECT dwEffect);

[PreserveSig]
HRESULT DragLeave();

[PreserveSig]
HRESULT DragOver(
ref Point ppt,
Ole32.DROPEFFECT dwEffect);

[PreserveSig]
HRESULT Drop(
IComDataObject pDataObj,
ref Point ppt,
Ole32.DROPEFFECT dwEffect);

[PreserveSig]
HRESULT Show(
BOOL fShow);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Drawing;
using System.Runtime.InteropServices;
using static Interop.Gdi32;

internal partial class Interop
{
internal static partial class Shell32
{
[StructLayout(LayoutKind.Sequential)]
public struct SHDRAGIMAGE
{
public Size sizeDragImage;
public Point ptOffset;
public HBITMAP hbmpDragImage;
public COLORREF crColorKey;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

internal partial class Interop
{
internal unsafe partial class WinFormsComWrappers
{
internal static class IDropSourceNotifyVtbl
{
public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease)
{
IntPtr* vtblRaw = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IDropSourceNotifyVtbl), IntPtr.Size * 5);
vtblRaw[0] = fpQueryInterface;
vtblRaw[1] = fpAddRef;
vtblRaw[2] = fpRelease;
vtblRaw[3] = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, HRESULT>)&DragEnterTarget;
vtblRaw[4] = (IntPtr)(delegate* unmanaged<IntPtr, HRESULT>)&DragLeaveTarget;

return (IntPtr)vtblRaw;
}

[UnmanagedCallersOnly]
private static HRESULT DragEnterTarget(IntPtr thisPtr, IntPtr hwndTarget)
{
try
{
var instance = ComInterfaceDispatch.GetInstance<Ole32.IDropSourceNotify>((ComInterfaceDispatch*)thisPtr);
return instance.DragEnterTarget(hwndTarget);
}
catch (Exception ex)
{
return (HRESULT)ex.HResult;
}
}

[UnmanagedCallersOnly]
private static HRESULT DragLeaveTarget(IntPtr thisPtr)
{
try
{
var instance = ComInterfaceDispatch.GetInstance<Ole32.IDropSourceNotify>((ComInterfaceDispatch*)thisPtr);
return instance.DragLeaveTarget();
}
catch (Exception ex)
{
return (HRESULT)ex.HResult;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,13 @@ private WinFormsComWrappers() { }
GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease);

IntPtr iDropSourceVtbl = IDropSourceVtbl.Create(fpQueryInterface, fpAddRef, fpRelease);
IntPtr iDropSourceNotifyVtbl = IDropSourceNotifyVtbl.Create(fpQueryInterface, fpAddRef, fpRelease);

ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(WinFormsComWrappers), sizeof(ComInterfaceEntry));
wrapperEntry->IID = IID.IDropSource;
wrapperEntry->Vtable = iDropSourceVtbl;
ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(WinFormsComWrappers), sizeof(ComInterfaceEntry) * 2);
wrapperEntry[0].IID = IID.IDropSource;
wrapperEntry[0].Vtable = iDropSourceVtbl;
wrapperEntry[1].IID = IID.IDropSourceNotify;
wrapperEntry[1].Vtable = iDropSourceNotifyVtbl;
return wrapperEntry;
}

Expand Down Expand Up @@ -130,7 +133,7 @@ private WinFormsComWrappers() { }

if (obj is Ole32.IDropSource)
{
count = 1;
count = 2;
return s_dropSourceEntry;
}

Expand Down
27 changes: 26 additions & 1 deletion src/System.Windows.Forms/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
~override System.Windows.Forms.WebBrowser.CreateAccessibilityInstance() -> System.Windows.Forms.AccessibleObject
System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Invalid = -1 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.None = 0 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Copy = 1 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Move = 2 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Link = 4 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Label = 6 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.Warning = 7 -> System.Windows.Forms.DropImageType
System.Windows.Forms.DropImageType.NoImage = 8 -> System.Windows.Forms.DropImageType
~System.Windows.Forms.Control.DoDragDrop(object data, System.Windows.Forms.DragDropEffects allowedEffects, System.Drawing.Bitmap dragImage, System.Drawing.Point cursorOffset, bool useDefaultDragImage) -> System.Windows.Forms.DragDropEffects
~System.Windows.Forms.ToolStripItem.DoDragDrop(object data, System.Windows.Forms.DragDropEffects allowedEffects, System.Drawing.Bitmap dragImage, System.Drawing.Point cursorOffset, bool useDefaultDragImage) -> System.Windows.Forms.DragDropEffects
System.Windows.Forms.DragEventArgs.DragEventArgs(System.Windows.Forms.IDataObject? data, int keyState, int x, int y, System.Windows.Forms.DragDropEffects allowedEffect, System.Windows.Forms.DragDropEffects effect, System.Windows.Forms.DropImageType dropImageType, string! message, string! messageReplacementToken) -> void
System.Windows.Forms.DragEventArgs.DropImageType.get -> System.Windows.Forms.DropImageType
System.Windows.Forms.DragEventArgs.DropImageType.set -> void
System.Windows.Forms.DragEventArgs.Message.get -> string?
System.Windows.Forms.DragEventArgs.Message.set -> void
System.Windows.Forms.DragEventArgs.MessageReplacementToken.get -> string?
System.Windows.Forms.DragEventArgs.MessageReplacementToken.set -> void
System.Windows.Forms.GiveFeedbackEventArgs.GiveFeedbackEventArgs(System.Windows.Forms.DragDropEffects effect, bool useDefaultCursors, System.Drawing.Bitmap? dragImage, System.Drawing.Point cursorOffset, bool useDefaultDragImage) -> void
System.Windows.Forms.GiveFeedbackEventArgs.CursorOffset.get -> System.Drawing.Point
System.Windows.Forms.GiveFeedbackEventArgs.CursorOffset.set -> void
System.Windows.Forms.GiveFeedbackEventArgs.DragImage.get -> System.Drawing.Bitmap?
System.Windows.Forms.GiveFeedbackEventArgs.DragImage.set -> void
System.Windows.Forms.GiveFeedbackEventArgs.UseDefaultDragImage.get -> bool
System.Windows.Forms.GiveFeedbackEventArgs.UseDefaultDragImage.set -> void
~override System.Windows.Forms.WebBrowser.CreateAccessibilityInstance() -> System.Windows.Forms.AccessibleObject
47 changes: 43 additions & 4 deletions src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5285,8 +5285,34 @@ internal virtual void DisposeAxControls()
/// </summary>
public DragDropEffects DoDragDrop(object data, DragDropEffects allowedEffects)
{
Ole32.IDropSource dropSource = new DropSource(this);
return DoDragDrop(data, allowedEffects, dragImage: null, cursorOffset: default, useDefaultDragImage: false);
}

/// <summary>
/// Begins a drag operation. The <paramref name="allowedEffects"/> determine which drag operations can occur. If the drag operation
/// needs to interop with applications in another process, <paramref name="data"/> should either be a base managed class
/// (<see cref="string"/>, <see cref="Bitmap"/>, or <see cref="Drawing.Imaging.Metafile"/>) or some <see cref="object"/> that implements
/// <see cref="Runtime.Serialization.ISerializable"/>. <paramref name="data"/> can also be any <see cref="object"/> that implements
/// <see cref="IDataObject"/>. <paramref name="dragImage"/> is the bitmap that will be displayed during the drag operation and
/// <paramref name="cursorOffset"/> specifies the location of the cursor within <paramref name="dragImage"/>, which is an offset from the
/// upper-left corner. Specify <see langword="true"/> for <paramref name="useDefaultDragImage"/> to use a layered window drag image with a
/// size of 96x96; otherwise <see langword="false"/>. Note the outer edges of <paramref name="dragImage"/> are blended out if the image width
/// or height exceeds 300 pixels.
/// </summary>
/// <returns>
/// A value from the <see cref="DragDropEffects"/> enumeration that represents the final effect that was performed during the drag-and-drop
/// operation.
/// </returns>
/// <remarks>
/// <para>
/// Because <see cref="DoDragDrop(object, DragDropEffects, Bitmap, Point, bool)"/> always performs the RGB multiplication step in calculating
/// the alpha value, you should always pass a <see cref="Bitmap"/> without premultiplied alpha blending. Note that no error will result from
/// passing a <see cref="Bitmap"/> with premultiplied alpha blending, but this method will multiply it again, doubling the resulting alpha
/// value.
/// </para>
/// </remarks>
public DragDropEffects DoDragDrop(object data, DragDropEffects allowedEffects, Bitmap dragImage, Point cursorOffset, bool useDefaultDragImage)
{
IComDataObject dataObject = null;

if (data is IComDataObject)
Expand All @@ -5309,10 +5335,23 @@ public DragDropEffects DoDragDrop(object data, DragDropEffects allowedEffects)
dataObject = (IComDataObject)iwdata;
}

HRESULT hr = Ole32.DoDragDrop(dataObject, dropSource, (Ole32.DROPEFFECT)allowedEffects, out Ole32.DROPEFFECT finalEffect);
if (!hr.Succeeded())
Ole32.DROPEFFECT finalEffect;

try
{
return DragDropEffects.None;
Ole32.IDropSource dropSource = new DropSource(this, dataObject, dragImage, cursorOffset, useDefaultDragImage);
HRESULT hr = Ole32.DoDragDrop(dataObject, dropSource, (Ole32.DROPEFFECT)allowedEffects, out finalEffect);
if (!hr.Succeeded())
{
return DragDropEffects.None;
}
}
finally
{
if (DragDropHelper.IsInDragLoop(dataObject))
{
DragDropHelper.SetInDragLoop(dataObject, inDragLoop: false);
}
}

return (DragDropEffects)finalEffect;
Expand Down
Loading

0 comments on commit 180639b

Please sign in to comment.