C# - Winforms - 触摸拖动时不显示自定义拖动图像

C# - Winforms - Custom Drag Image is Not Displayed When Dragging by Touch

我有一个用 C# 编写的具有自定义拖放功能的旧 WinForms 应用程序。此应用程序在触摸屏设备(例如 Surface Pro 3)上 运行 时会出现问题。

基本上有一个树视图控件,允许将项目拖到应用程序的不同区域并完成一些计算。如果我使用鼠标或手写笔,则会在屏幕上绘制自定义拖动图像。如果我使用触摸拖动项目,图像不会显示,但代码会正确执行,包括自定义光标的绘制。

似乎自定义拖动图像没有显示,因为鼠标光标被O.S隐藏了。在触摸拖动操作期间。如何显示拖动图像?

更新 这是一些代码来演示我要修复的问题。创建一个新的 WinForms 应用程序,向其添加一个树视图并连接事件。您会注意到,如果您使用手写笔或鼠标,拖动操作将显示一个图标。如果您触摸并拖动某个项目,则不会显示任何内容。

public Form1()
    {
        InitializeComponent();

        TreeNode node = new TreeNode("welp");

        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
        treeView1.Nodes.Add(node);
    }

    private void treeView1_DragEnter(object sender, DragEventArgs e)
    {
        e.Effect = DragDropEffects.Move;
    }

    private void treeView1_GiveFeedback(object sender, GiveFeedbackEventArgs e)
    {
        e.UseDefaultCursors = true;
    }

    private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
    {
        ((TreeView)sender).DoDragDrop(e.Item, DragDropEffects.Move);
    }

好的,所以我完全按照我想要的方式工作了。这并不容易,它可能不是解决它的首选方法,但它做了我想要的,我正赶时间完成它。

首先,我找到了这篇文章Shell Style Drag and Drop。它在大多数情况下都有效,但它是大约 7 年前写的,并没有考虑 O.S 是如何工作的。已经改变。关于它的细节以及为什么它对我不起作用的细节太多了。

我试图解决的主要问题是我原来的拖放代码使用了在拖放操作的 GiveFeedback 事件中绘制新光标的老派方法。由于 Windows O.S,在触摸设备上工作得很好。在触摸拖动操作期间隐藏光标,这样我自定义绘制的光标将永远不会显示。一些(轻度使用)Google 搜索显示 Windows 不允许两个指针设备同时处于活动状态,即使调用本机 ShowCursor 方法也是如此。很公平。

在拖放操作中尝试在屏幕上绘图的最大问题是大多数发送到 window 的消息,例如 WM_MOUSEMOVE 和 WM_PAINT被暂停。我可以画任何东西的唯一方法是在 GiveFeedback 事件中。

所以,我发现这个 article 涵盖了使用透明覆盖层绘制的内容。同样,它并没有完全按照我的要求进行操作,但稍加调整就可以完美运行。我能够获取叠加层的 HDC 和我的表单的 HDC,并执行一个很好的 BitBlt 操作以在屏幕上显示我的自定义位图,并通过获取表单的底层图像来擦除旧绘图。我还将绘制自定义光标的方法更改为绘制自定义位图的方法。

这是我对上述文章所做的更改

this.BackColor = Color.White;
this.Opacity = 1;      // Tweak as desired
this.TransparencyKey = Color.White;

以及我为允许消息泵通过而添加的内容

protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= Win32.WS_EX_TRANSPARENT;

            return createParams;
        }
    }

其余的细节并不过分复杂,但需要很好地混合拖放操作、叠加、绘图和擦除以及双缓冲。在某些情况下,就像我的情况一样,我在拖动时更新了 TreeView 控件,我必须扩展绘制操作的矩形,以便 bitblt 图像覆盖我绘图之外的区域。

如果您对本文感兴趣并想了解我做了什么以及如何完成它,请在评论中告诉我,我将 post 编写代码。

试试这个代码 希望对你有帮助

 public Form1()
        {
            InitializeComponent();
            TreeNode node;
            for (int x = 0; x < 3; ++x)
            {

                node = treeView1.Nodes.Add(String.Format("Node{0}", x * 4));
                for (int y = 1; y < 4; ++y)
                {

                    node = node.Nodes.Add(String.Format("Node{0}", x * 4 + y));
                }
            }

            treeView1.AllowDrop = true;
            treeView1.Dock = DockStyle.Fill;
            treeView1.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag);
            treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
            treeView1.DragOver += new DragEventHandler(treeView1_DragOver);
            treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
        }
        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                DoDragDrop(e.Item, DragDropEffects.Move);
            }

            else if (e.Button == MouseButtons.Right)
            {
                DoDragDrop(e.Item, DragDropEffects.Copy);
            }
        }

        private void treeView1_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = e.AllowedEffect;
        }
        private void treeView1_DragOver(object sender, DragEventArgs e)
        {
            Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
            treeView1.SelectedNode = treeView1.GetNodeAt(targetPoint);
        }

        private void treeView1_DragDrop(object sender, DragEventArgs e)
        {
            Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
            TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
            TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));      
            if (!draggedNode.Equals(targetNode) && !ContainsNode(draggedNode, targetNode))
            {
                if (e.Effect == DragDropEffects.Move)
                {
                    draggedNode.Remove();
                    targetNode.Nodes.Add(draggedNode);
                }
                else if (e.Effect == DragDropEffects.Copy)
                {
                    targetNode.Nodes.Add((TreeNode)draggedNode.Clone());
                }

                targetNode.Expand();
            }
        }

        private bool ContainsNode(TreeNode node1, TreeNode node2)
        {
            if (node2.Parent == null) return false;
            if (node2.Parent.Equals(node1)) return true;
            return ContainsNode(node1, node2.Parent);
        }