简体   繁体   English

如何在WinForms中实现常见的“拖放”图标

[英]How to implement the common “Drag and Drop” icon in WinForms

I'm currently designing a simple WinForms UserControl in C# where a user can drag and drop an excel file onto a panel instead of browsing for the file. 我目前正在用C#设计一个简单的WinForms UserControl,用户可以在其中将excel文件拖放到面板上,而不用浏览该文件。 I have it technically working, but it's very crude. 从技术上讲,我可以使用它,但是它非常粗糙。

In short, my code currently looks like this for the DragEnter and DragDrop events on the panel (removed error handling): 简而言之,对于面板上的DragEnter和DragDrop事件,我的代码当前看起来像这样(已删除错误处理):

    private void dragPanel_DragEnter(object sender, DragEventArgs e)
    {
        var filenames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
        if (Path.GetExtension(filenames[0]) == ".xlsx") e.Effect = DragDropEffects.All;
        else e.Effect = DragDropEffects.None;
    }

    private void dragPanel_DragDrop(object sender, DragEventArgs e)
    {
        var filenames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
        string filename = filenames[0];
        // Do stuff
    }

I'm trying to get the Excel icon to show up as I drag the file, but all I can get is this thing: 我正在尝试在拖动文件时显示Excel图标,但是我能得到的只是这个东西: 在此处输入图片说明

Anywhere I've looked online (mostly on this forum) has said I need to implement my own custom cursor if I want a specific icon to show up, but I honestly don't believe that. 我在网上看过的所有地方(大多在这个论坛上)都说过,如果我想显示特定的图标,则需要实现自己的自定义光标,但老实说我不相信。 I took screenshots of multiple applications from different companies all using the exact same control (this is just a subset). 我截取了来自不同公司的多个应用程序的屏幕快照,全部使用完全相同的控件(这只是其中的一个子集)。 Note that none of them are even cursors, the icons just follow the cursor: 请注意,它们都不是光标,图标仅跟随光标:

Windows Explorer: Windows资源管理器:

在此处输入图片说明

Google Chrome: 谷歌浏览器:

在此处输入图片说明

Adobe Acrobat: Adobe Acrobat:

在此处输入图片说明

Microsoft Edge: Microsoft Edge:

(Same icon, but DragDropEffects are likely set to None) (相同的图标,但DragDropEffects可能设置为“无”)

在此处输入图片说明

So my conclusion is there must be a common windows control for this, but where is it? 因此,我的结论是必须有一个通用的Windows控件,但是它在哪里? There's no way all of these companies just coincidentally built the exact same design and functionality! 所有这些公司都不可能巧合地构建完全相同的设计和功能!

Any help would be appreciated! 任何帮助,将不胜感激!

Bonus Question: Apparently in Windows 10 you're not allowed to drag-and-drop onto a program that's running as Administrator, however Chrome definitely lets you do this. 奖励问题:显然,在Windows 10中,您不允许拖放到以Administrator身份运行的程序上,但是Chrome绝对可以做到这一点。 You can run Chrome as Admin and drag a file onto it without any issue. 您可以以管理员身份运行Chrome并将文件拖到其上,而不会出现任何问题。 What magic did Google use to bypass this security feature? Google使用什么魔术来绕过此安全功能? I'd like to implement it as well as my control could potentially be used inside a program running as admin. 我想实现它,并且我的控件可以在以admin身份运行的程序中使用。

Standard way to do that is to delegate drop icon rendering DragDropHelper COM Object provided by Shell. 执行此操作的标准方法是委派由Shell提供的放置图标渲染DragDropHelper COM对象

It allows application to negotiate image and icon to be displayed. 它允许应用程序协商要显示的图像和图标。 In your case, Explorer already uses IDragSourceHelper for drag icon negotiation, so all you have to do is to delegate drop events to IDropTargetHelper exposed by DragDropHelper : 在您的情况下,Explorer已经使用IDragSourceHelper进行拖动图标协商,因此您要做的就是将放置事件委托给DragDropHelper公开的IDropTargetHelper

Interop: 互操作性:

using IDataObject_Com = System.Runtime.InteropServices.ComTypes.IDataObject;

[StructLayout(LayoutKind.Sequential)]
public struct Win32Point
{
    public int x;
    public int y;
}

[ComImport]
[Guid("4657278A-411B-11d2-839A-00C04FD918D0")]
public class DragDropHelper { }

[ComVisible(true)]
[ComImport]
[Guid("4657278B-411B-11D2-839A-00C04FD918D0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropTargetHelper
{
    void DragEnter(
        [In] IntPtr hwndTarget,
        [In, MarshalAs(UnmanagedType.Interface)] IDataObject_Com dataObject,
        [In] ref Win32Point pt,
        [In] int effect);
    void DragLeave();

    void DragOver(
        [In] ref Win32Point pt,
        [In] int effect);

    void Drop(
        [In, MarshalAs(UnmanagedType.Interface)] IDataObject_Com dataObject,
        [In] ref Win32Point pt,
        [In] int effect);

    void Show(
        [In] bool show);
}

Form: 形成:

private IDropTargetHelper ddHelper = (IDropTargetHelper)new DragDropHelper();

private void Form1_DragDrop(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
    Point p = Cursor.Position;
    Win32Point wp;
    wp.x = p.X;
    wp.y = p.Y;

    ddHelper.Drop(e.Data as IDataObject_Com, ref wp, (int)e.Effect);
}

private void Form1_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
    Point p = Cursor.Position;
    Win32Point wp;
    wp.x = p.X;
    wp.y = p.Y;

    ddHelper.DragEnter(this.Handle, e.Data as IDataObject_Com, ref wp, (int)e.Effect);
}

private void Form1_DragLeave(object sender, EventArgs e)
{
    ddHelper.DragLeave();
}

private void Form1_DragOver(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
    Point p = Cursor.Position;
    Win32Point wp;
    wp.x = p.X;
    wp.y = p.Y;

    ddHelper.DragOver(ref wp, (int)e.Effect);
}

WPF Version is basically the same, with minor changes: WPF版本基本上是相同的,但有一些小的更改:

private void Window_DragEnter(object sender, DragEventArgs e)
{
    e.Effects = DragDropEffects.Copy;
    e.Handled = true;
    Point p = this.PointToScreen(e.GetPosition(this));
    Win32Point wp;
    wp.x = (int)p.X;
    wp.y = (int)p.Y;
    ddHelper.DragEnter(new WindowInteropHelper(this).Handle, e.Data as IDataObject_Com, ref wp, (int)e.Effects);
}

References: 参考文献:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM