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

8
我目前正在使用C#设计一个简单的WinForms UserControl,用户可以将Excel文件拖放到面板上,而不是浏览文件。我已经在技术上实现了这个功能,但它非常粗糙。
简而言之,我的代码目前看起来像这样,在面板上的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
    }

我试图在拖动文件时显示Excel图标,但我只能得到这个东西:enter image description here。无论我在哪里搜索(主要是在此论坛上),都说如果想显示特定的图标,则需要实现自己的自定义光标,但我真的不相信这一点。我从不同公司的多个应用程序中截取了多个屏幕截图,所有应用程序都使用完全相同的控件(这只是其中的一个子集)。请注意,它们都不是光标,图标只是跟随光标移动:
Windows资源管理器: enter image description here Google Chrome: enter image description here Adobe Acrobat: enter image description here Microsoft Edge: (相同的图标,但DragDropEffects可能设置为None) enter image description here 因此,我的结论是必须有一个通用的Windows控件,但它在哪里?所有这些公司不可能巧合地构建完全相同的设计和功能!
任何帮助都将不胜感激!
附加问题:显然,在Windows 10中,您不被允许将文件拖放到以管理员身份运行的程序上,但是Chrome肯定可以做到这一点。您可以将Chrome作为管理员运行并将文件拖放到其中而没有任何问题。谷歌使用了什么魔法来绕过此安全功能?我想实现它,因为我的控件可能会在以管理员身份运行的程序中使用。
1个回答

6

标准的做法是通过委托由Shell提供的DragDropHelper COM对象来渲染拖放图标。

它允许应用程序协商要显示的图像和图标。在您的情况下,资源管理器已经使用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);
}

表单:

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版本基本相同,只有一些微小的变化:

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

参考文献:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接