07 Jul 2008

C#: Drag and Drop – Part 1

4 Comments Programming

Drag and drop is one of the things that seems easier in C# than Java. In OnTime, the project I'm currently working on, I've used it in several places to make the UI easier to use.

The first example I want to cover is dragging between a ListView and TreeView. This could be used to make re-arranging things easier. Eg: If you're displaying categories in a tree and items in a list.

  1. First you need to start a drag and drop operation when a ListViewItem is dragged from the ListView. Do this by assigning a delegate to listen for the ItemDrag event.

    listView1.ItemDrag += delegate(object sender, ItemDragEventArgs e) {
      List<string> itemIDs = new List<string>((sender as ListView).SelectedItems.Count);
      foreach(ListViewItem item in (sender as ListView).SelectedItems) {
        itemIDs.Add(item.Name);
      }
      (sender as ListView).DoDragDrop(itemIDs.ToArray(), DragDropEffects.Move);
    };

    This iterates through all selected items in the ListView and adds them to an array for later processing.

  2. Data can only be dropped onto the TreeView if it's set to accept drops, so set it's AllowDrop property to True.

  3. This will start a drag and drop operation when you drag an item in the ListView, but if you try to run the code, you'll get a not allowed cursor because the TreeView doesn't know how to handle drop events yet.

  4. Now you need to set a drag effect when the cursor moves over the Tree.

    treeView1.DragEnter += delegate(object sender, DragEventArgs e) {
        e.Effect = DragDropEffects.Move;
    };
  5. Nodes in the tree aren't highlighted automatically when you drag data over them, so to aid in dropping onto the right node, the DragOver is raised as the cursor moves over the TreeView.

    private TreeNode hoverNode; // node being hovered over during DnD
     
    treeCats.DragOver += delegate(object sender, DragEventArgs e) {
      Point mouseLocation = treeCats.PointToClient(new Point(e.X, e.Y));
      TreeNode node = treeCats.GetNodeAt(mouseLocation);
      if(node != null) {
        if(hoverNode == null) {
          node.BackColor = Color.LightBlue;
          node.ForeColor = Color.White;
          hoverNode = node;
        } else if(hoverNode != node) {
          hoverNode.BackColor = Color.White;
          hoverNode.ForeColor = Color.Black;
          node.BackColor = Color.LightBlue;
          node.ForeColor = Color.White;
          hoverNode = node;
        }
      }
    };

    This just checks which node the cursor is hovering over and makes it appear selected if it's not null (if the mouse is over an empty area). The node's value is stored in a variable and if the current node is different from the stored variable, indicating the mouse is over a new node, the old node's colours are reset and the new node is coloured to appear selected.

  6. So now the ListView will start a drag drop operation when an item is dragged and the TreeView will highlight nodes as the mouse passes over them, but nothing happens if data is actually dropped onto a node. We need to assign a delegate to listen to the DragDrop event, which is raised when data is dropped.

    treeView1.DragDrop += delegate(object sender, DragEventArgs e) {
      if(e.Data.GetDataPresent(typeof(string[]))) {
        Point dropLocation = (sender as TreeView).PointToClient(new Point(e.X, e.Y));
        TreeNode dropNode = (sender as TreeView).GetNodeAt(dropLocation);
        string[] IDs = (string[])e.Data.GetData(typeof(string[]));
        ChangeCategory(IDs, dropNode.Name);
      }
      ResetTreeColors(null, EventArgs.Empty); // 
    };

    The ResetTreeColors method is covered later. First we check to see whether the data which has been dropped is a string array, since we started the operation with an array of strings in the ListView's ItemDrag event. If the data which has been dropped is a string array, we first need to determine where the data was dropped. The TreeView's GetNodeAt(Point p) helps with this. Once you have the node the data was dropped on and the data itself, you just need to process it.

    In this example, I passed the string array and the name of the node to another method which the processing.

  7. The ResetTreeColors method I mentioned earlier just iterates through every node in the tree and resets it's fore and background colours. You can also add this method as an EventHandler for the DragLeave event, which resets the target node after the mouse focus leave the control (If the user cancels or completes the drop).

    treeCats.DragLeave += new EventHandler(ResetTreeColors);
     
    private void ResetTreeColors(object sender, EventArgs e) {
      foreach(TreeNode node in treeCats.Nodes[0].Nodes) {
        node.BackColor = Color.White;
        node.ForeColor = Color.Black;
      }
    }

This is fine for TreeViews, ListViews, and other controls which raise the ItemDrag event, but for user controls, you have to implement it yourself. I was planning to run through that as well, but the post was a big longer than expected so I'll save it for another day.

C#:Drag and Drop – Part 2

Tags: , , , , , , , ,
written by
The author didn‘t add any Information to his profile yet.

4 Responses to “C#: Drag and Drop – Part 1”

  1. Reply chichoe2008 says:

    I don’t find the method “ChangeCategory”. Could you show to me, please ?

  2. Reply Echilon says:

    That’s just a method I used to do something with the node which was dropped. You can replace it with your own method or do somthing with the node directly in the delegate.

  3. Reply rx8wej says:

    This is a good tutorial, very helpful, but I can’t figure out how to reset the colors on the highlighted node if someone drops something that is not a string[]; i.e. if they drag an icon from explorer into my application. The dragleave event does not fire. Also, if you drag over an area below the bottom node and drop, dropNode is null. I can handle that, but no matter what I do, if I check if it is null, it still will not reset the Node colors. I modified your DragLeave code slightly to loop through all nodes and child nodes, but otherwise it is the same as your code, resetting the colors. Is there a way to reset the colors when a bad drop occurs?

Leave a Reply