3

I have an old WinForms app written in C# with custom drag and drop capabilities. And this app has an issue when it is run on a touch screen device, such as the Surface Pro 3.

Basically there is a treeview control that allows the items to be dragged to a different area of the app and have some calculations done. If I use the mouse or the stylus the custom drag image is drawn on the screen. If I drag an item using touch the image is not displayed but the code is executed properly, including the drawing of a custom cursor.

It seems that the custom drag image is not displayed because the mouse cursor is hidden by the O.S. during a touch drag operation. How do I get the drag image to display?

UPDATE Here is some code to demonstrate what I am trying to fix. Create a new WinForms app, add a treeview to it and wire up the events. You'll notice that if you use a stylus or the mouse the drag operation will show an icon. If you touch-drag an item, nothing shows.

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);
    }
BeanFlicker
  • 503
  • 1
  • 9
  • 23

2 Answers2

1

Ok, So I got this working exactly the way that I want it to. Wasn't easy and it's probably not the preferred way to go about it but it does what I want and I was on a time crunch to get this finished.

First, I found this article Shell Style Drag and Drop. It worked, for the most part, but it was written about 7 years ago and doesn't take into consideration how the O.S. has changed. Too much to go into the details about that and why it didn't work for me.

The main issue I was trying to fix was that my original drag and drop code used the old school way of drawing a new cursor in the GiveFeedback event of a drag and drop operation. Worked great except on touch devices because the Windows O.S. hid the cursor during a touch-drag operation so my custom drawn cursor would never be displayed. A few (used lightly) Google searches revealed that Windows won't allow two pointer devices to be active at the same time, even with calling the native ShowCursor method. Fair enough.

The biggest issue with trying to draw on the screen while in a drag and drop operation is that most of the messages sent to a window, such as WM_MOUSEMOVE and WM_PAINT are suspended. The only way I could draw anything was in the GiveFeedback event.

So, I found this article that covers drawing using a transparent overlay. Again, it didn't do exactly what I wanted but with a little tweaking it worked perfectly. I was able to grab the HDC of the overlay and the HDC of my form and do a nice BitBlt operation to display my custom bitmap on the screen and erase the old drawing by grabbing the underlying image of the form. I also changed the method that draws a custom cursor to a method that draws a custom bitmap.

Here is what I changed in regards to the article above

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

And what I added to allow the message pump to go through

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

            return createParams;
        }
    }

The details of the rest are not overly complex but require a nice blending of the drag and drop operations, the overlay, drawing and erasing and double buffering. And there are situations, like in my case, where I have a TreeView control updating while I am dragging and I have to expand the rectangle of the draw operation so the bitblt image covers the areas outside of my drawing.

If you are interested in this article and want to see what I did and how I accomplished it, let me know in the comments and I will post code.

Community
  • 1
  • 1
BeanFlicker
  • 503
  • 1
  • 9
  • 23
  • Unfortunately, I want to know. Can you post the code? Unless you've found a more straight-forward way of doing this since then? – osoblanco Jan 06 '20 at 19:34
  • The link posted there doesn't exist anymore. If you still have the code please post it :) – russelrillema Jul 20 '22 at 14:12
  • There are three links in this article. I assume you mean the first one? Yeah, that was from some guy's personal blog from 14 years ago. Seems like it's gone for good. – BeanFlicker Jul 20 '22 at 18:26
0

try this code hope is helped you

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