How do I implement Drag and Drop support between ListBoxes

Platform: WinForms| Category: ListBox

The code below minimally handles D&D for a single selection list box by handling four events. The D&D is initiated in MouseDown if the user mouses down on the current selection. The DragEnter event is used to set the dragging effect to copy if you are not over the drag source. The DragDrop event is used to do the drop. And finally, the SelectedIndexChanged event is used to track the current selection for use in the MouseDown event. You can download a working project (C#, VB). This project handles additional events to provide visual queues during the drop.

The samples referenced above do not allow dragging and dropping within the same listbox. Here is another set of samples that does allow you to drag and drop within the same listbox.

public class ListBoxDragNDrop : ListBox
{
  private int lastMouseUpItemIndex = -1;
  private bool isDropSource = false;

  public ListBoxDragNDrop()
  {
    this.AllowDrop = true;  //allow D&D
    this.SelectionMode = SelectionMode.One;   //single selection

    DragDrop += new System.Windows.Forms.DragEventHandler(OnDragDrop);
    DragEnter += new System.Windows.Forms.DragEventHandler(OnDragEnter);
    MouseDown += new System.Windows.Forms.MouseEventHandler(OnMouseDown);
    SelectedIndexChanged += new System.EventHandler(OnSelectedIndexChanged);
  }
      
  private void OnDragDrop(object sender, DragEventArgs e)
  {
    if(e.Effect == DragDropEffects.Copy)
    {
      Point point = this.PointToClient(new Point(e.X, e.Y));
      int index = this.IndexFromPoint(point);
      if( index > -1 && index < this.Items.Count)
        Items.Insert(index, e.Data.GetData(DataFormats.Text));
      else
        Items.Add(e.Data.GetData(DataFormats.Text));
    }
  }

  private void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
  {
    if (e.Data.GetDataPresent(DataFormats.Text) && !isDropSource ) 
      e.Effect = DragDropEffects.Copy; 
    else 
      e.Effect = DragDropEffects.None; 
  } 

  private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
  {
    if(MouseButtons == MouseButtons.Left && SelectedIndex == lastMouseUpItemIndex)
    {
      isDropSource = true;
      DoDragDrop(SelectedItem, DragDropEffects.Copy);
      isDropSource = false; 
      lastMouseUpItemIndex = this.SelectedIndex;
    }
  }

  private void OnSelectedIndexChanged(object sender, System.EventArgs e)
  {
    lastMouseUpItemIndex = this.SelectedIndex;
  }
}

One more note. If your listboxes contain full file pathnames, you can support dropping these paths onto Windows Explorer by supporting the FileDrop dataformat. In the OnMouseDown override, if you replace the DoDragDrop line with the following code block, you will be able to drop files.

  //put the file path is a string array
  string[] files = new String[1];
  files[0] = SelectedItem.ToString();

  //create a dataobject holding this array as a filedrop
  DataObject data = new DataObject(DataFormats.FileDrop, files);

  //also add the selection as textdata
  data.SetData(DataFormats.StringFormat, SelectedItem.ToString());

  //do the dragdrop
  DoDragDrop(data, DragDropEffects.Copy);

Share with

Related FAQs

Couldn't find the FAQs you're looking for?

Please submit your question and answer.