Answered Drag an Drop on single/multiple selection

aw48

Active member
Joined
Feb 9, 2013
Messages
36
Location
Germany
Programming Experience
10+
hi,
I'm using VS10 c#, Framework 4.0
I want to move Data from ListView to ListBox, but type of control doesn't matter
Can somebody explain how to use the corresponding events to move selected data from one control to another like moving data in windowsexplorer from one Folder to another.
for the listview I have the mouseevents down, move, up.
for the listbox I have the drag-drop events dragenter and dragdrop
please do not post code, only description where which action is required.
One problem I have, for example is that I have to click the listview item twice before drag and drop to the listbox. first to select the item, second to start draganddrop
Any help appreciated. thanks in advance
 
Don't post code?

Ok

Well I'd link you to the relevant documentation, but they to provide code examples. Guess that sums up answering this topic.
 
I will reproduce some posts below that I submitted elsewhere that may help you understand the principles involved and the steps that you can perform to get drag and drop working in any specific scenario.
 
If you're interested in performing drag & drop operations in WinForms then I suggest that you read this and this at least.

I'll start with moving items between two ListBoxes, which is relatively simple.

1. Create a new WinForms application project.
2. Add two ListBoxes to the form.
3. Replace the existing form code with the following:
C#:
public Form1()
{
    InitializeComponent();

    // Hook up the form event handler.
    this.Load += new EventHandler(Form1_Load);
}

private void Form1_Load(object sender, EventArgs e)
{
    // Allow data to be dropped on both ListBoxes.
    this.listBox1.AllowDrop = true;
    this.listBox2.AllowDrop = true;

    // Populate the ListBoxes.
    this.listBox1.Items.AddRange(new string[] {"List 1, Item 1",
                                               "List 1, Item 2",
                                               "List 1, Item 3",
                                               "List 1, Item 4",
                                               "List 1, Item 5"});
    this.listBox2.Items.AddRange(new string[] {"List 2, Item 1",
                                               "List 2, Item 2",
                                               "List 2, Item 3",
                                               "List 2, Item 4",
                                               "List 2, Item 5"});

    // Hook up the ListBox event handlers.
    this.listBox1.MouseDown += new MouseEventHandler(ListBox_MouseDown);
    this.listBox2.MouseDown += new MouseEventHandler(ListBox_MouseDown);
    this.listBox1.DragEnter += new DragEventHandler(ListBox_DragEnter);
    this.listBox2.DragEnter += new DragEventHandler(ListBox_DragEnter);
    this.listBox1.DragDrop += new DragEventHandler(ListBox_DragDrop);
    this.listBox2.DragDrop += new DragEventHandler(ListBox_DragDrop);
}

void ListBox_MouseDown(object sender, MouseEventArgs e)
{
    ListBox source = (ListBox)sender;

    for (int index = 0; index < source.Items.Count; index++)
    {
        // Test whether the mouse location is within an item
        if (source.GetItemRectangle(index).Contains(e.Location))
        {
            // The mouse was depressed on an item so allow a move operation.
            source.DoDragDrop(source, DragDropEffects.Move);

            break;
        }
    }
}

void ListBox_DragEnter(object sender, DragEventArgs e)
{
    ListBox source = (ListBox)sender;

    if (e.Data.GetDataPresent("System.Windows.Forms.ListBox", false) &&
        e.Data.GetData("System.Windows.Forms.ListBox", false) != source)
    {
        // The data being dragged is a ListBox but not the one that was just entered.
        e.Effect = DragDropEffects.Move;
    }
}

void ListBox_DragDrop(object sender, DragEventArgs e)
{
    ListBox source = (ListBox)sender;

    if (e.Data.GetDataPresent("System.Windows.Forms.ListBox", false))
    {
        // Get the ListBox that the data was dragged from.
        ListBox data = (ListBox)e.Data.GetData("System.Windows.Forms.ListBox", false);


        // Make sure we aren't trying to drag from and drop to the same control.
        if (data != source)
        {
            // Get the item that was dragged.
            object item = data.SelectedItem;

            // Remove the item from its original location.
            data.Items.Remove(item);

            // Get the current mouse location relative to the control being dropped on.
            Point location = source.PointToClient(new Point(e.X, e.Y));
            int dropIndex = -1;

            // Find the item over which the mouse was released.
            for (int index = 0; index < source.Items.Count; index++)
            {
                if (source.GetItemRectangle(index).Contains(location))
                {
                    dropIndex = index;

                    break;
                }
            }

            if (dropIndex == -1)
            {
                // The mouse was not released over an item so add the new item to the end.
                source.Items.Add(item);
            }
            else
            {
                // Insert the new item above the item it was dropped on.
                source.Items.Insert(dropIndex, item);
            }
        }
    }
}
4. Run the project and start dragging and dropping.

I'll refine this example and add more over time. If there's anything you specifically want to see then post a request and I'll see if I can get to it.
 
This example demonstrates dragging one or more files from Windows Explorer, or some other application that supports dragging files, into a ListView.

1. Create a new WinForms project.
2. Add a ListView and an ImageList to the form.
3. Replace the contents of the form with the following code:
C#:
public Form1()
{
    InitializeComponent();

    this.Load += new EventHandler(Form1_Load);
}

void Form1_Load(object sender, EventArgs e)
{
    // Initialise the ListView.
    this.listView1.AllowDrop = true;
    this.listView1.Columns.Add("File name");
    this.listView1.Dock = DockStyle.Fill;
    this.listView1.SmallImageList = this.imageList1;
    this.listView1.View = View.Details;

    this.listView1.DragEnter += new DragEventHandler(listView1_DragEnter);
    this.listView1.DragDrop += new DragEventHandler(listView1_DragDrop);
}

void listView1_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent("FileDrop") &&
        (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
    {
        // A file list is being dragged and it can be copied so provide feedback to the user.
        e.Effect = DragDropEffects.Copy;
    }
}

void listView1_DragDrop(object sender, DragEventArgs e)
{
    // The data can only be dropped if it is a file list and it can be copied.
    if (e.Data.GetDataPresent("FileDrop") &&
        (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
    {
        // Get the data.
        string[] filePaths = (string[])e.Data.GetData("FileDrop");

        ListViewItem[] items = new ListViewItem[filePaths.Length];
        string filePath;

        // For each file in the list, create an item, complete with icon.
        for (int index = 0; index < filePaths.Length; index++)
        {
            filePath = filePaths[index];

            if (!this.imageList1.Images.Keys.Contains(filePath))
            {
                // This is the first time this file has been added so add its icon too.
                this.imageList1.Images.Add(filePath,
                                           Icon.ExtractAssociatedIcon(filePath));
            }

            items[index] = new ListViewItem(filePath, filePath);
        }

        // Add the items to the ListView.
        this.listView1.Items.AddRange(items);
    }
}
4. Run the project.
5. Select one or more files in Windows Explorer and drag them onto your form.

Tada! Note that, as before, I've done some setup of design time elements that you'd normally do in the designer. It was easier to do it in code than to explain though, for the purpose of this example.
 
I'm going to use that last example as a basis for showing you how to build up drag & drop code. I can hear people now saying "But how would I know to use the FileDrop format and how would I know that it returns a String array". Well, I'm going to show you how you work that out.

First up, you have to make sure that your control is a drop target, so set its AllowDrop property to True. Next you need to test what formats the data being dragged is available in. To do that you use the GetFormats method. GetFormats returns a String array, so you can loop through it to see what formats you can get the data in:
C#:
private void listView1_DragEnter(object sender As Object, DragEventArgs e)
{
    foreach (string format in e.Data.GetFormats())
    {
        MessageBox.Show(format);
    }
}
Drag your data onto the control and it will start popping up messages for each format. You might prefer to use Console.WriteLine and view the list in the Output window as it's less intrusive.

You now need to determine what .NET object will be created if you get the data in each of those formats and then decide which of those is most useful to you. To do that, you first need to get the data in each of those formats and then test it's type:
C#:
private void listView1_DragEnter(object sender, DragEventArgs e)
{
    foreach (string format in e.Data.GetFormats())
    {
        MessageBox.Show(e.Data.GetData(format).GetType().ToString(), format);
    }
}
You can then examine each of those objects based on its .NET type. Those that return a MemoryStream are going to be a little harder to interpret, but the String arrays are going to be easy to examine, so let's start with them:
C#:
private void listView1_DragEnter(object sender, DragEventArgs e)
{
    string[] strings;

    foreach (string format in e.Data.GetFormats())
    {
        strings = e.Data.GetData(format) as string();

        if (strings != null)
        {
            MessageBox.Show(String.Join(Environment.NewLine,
                                        strings),
                            format);
        }
    }
}
So, having done that by dragging multiple files you will now know that the FileName format returns the path of the first file in DOS-compatible format, the FileNameW format returns the full path of the first file and the FileDrop format returns the full paths of all the files. In this case that's as far as we need to go because the full paths of all files is exactly what we need. We now know that we need to test for the FileDrop format, which we do with GetDataPresent, and then get the data in that format, which we do with GetData.

That's basically how you should approach all drag & drop operations.
 
1. Create a new Windows Forms project.
2. Add a ListBox to the form.
3. Replace the existing code of the form with the following:
C#:
public Form1()
{
    InitializeComponent();

    this.Load += new EventHandler(Form1_Load);
}

private void Form1_Load(object sender, EventArgs e)
{
    // Initialise the ListBox.
    this.listBox1.AllowDrop = true;
    this.listBox1.Items.AddRange(new string[] {"Item 1",
                                               "Item 2",
                                               "Item 3",
                                               "Item 4",
                                               "Item 5"});

    this.listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
    this.listBox1.DragEnter += new DragEventHandler(listBox1_DragEnter);
    this.listBox1.DragOver += new DragEventHandler(listBox1_DragEnter);
    this.listBox1.DragDrop += new DragEventHandler(listBox1_DragDrop);
}

private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
    // Test whether the mouse is over an item.
    if (this.GetItemIndexAtPoint(e.Location) != -1)
    {
        // The mouse is over an item so start dragging.
        this.listBox1.DoDragDrop(this.listBox1, DragDropEffects.Move);
    }
}

private void listBox1_DragEnter(object sender, DragEventArgs e)
{
    // Test whether the data being dragged is the ListBox itself.
    if (e.Data.GetDataPresent("System.Windows.Forms.ListBox") &&
        e.Data.GetData("System.Windows.Forms.ListBox") == this.listBox1)
    {
        // Get the location of the mouse relative to the ListBox.
        Point mouseLocation = this.listBox1.PointToClient(new Point(e.X, e.Y));

        // Force the location to be within the horizontal bounds of the ListBox.
        if (mouseLocation.X < 0)
        {
            mouseLocation.X = 0;
        }
        else if (mouseLocation.X > this.listBox1.Width)
        {
            mouseLocation.X = this.listBox1.Width;
        }

        // Force the location to be within the vertical bounds of the ListBox.
        if (mouseLocation.Y < 0)
        {
            mouseLocation.Y = 0;
        }
        else if (mouseLocation.Y > this.listBox1.Height)
        {
            mouseLocation.Y = this.listBox1.Height;
        }

        if (this.GetItemIndexAtPoint(mouseLocation) == this.listBox1.SelectedIndex)
        {
            // Don't allow the selected item to be dropped on itself.
            e.Effect = DragDropEffects.None;
        }
        else
        {
            // Allow the selected item to be moved.
            e.Effect = DragDropEffects.Move;
        }
    }
}

private void listBox1_DragDrop(object sender, DragEventArgs e)
{
    // Test whether the data being dragged is the ListBox itself and the selected item can be moved.
    if (e.AllowedEffect == DragDropEffects.Move &&
        e.Data.GetDataPresent("System.Windows.Forms.ListBox") &&
        e.Data.GetData("System.Windows.Forms.ListBox") == this.listBox1)
    {
        int selectedIndex = this.listBox1.SelectedIndex;

        // Get the index of the item being dropped on.
        int dropIndex = this.GetItemIndexAtPoint(this.listBox1.PointToClient(new Point(e.X, e.Y)));

        // If the item being dropped on is below the selected item, the index of the
        // item being dropped on will decrement once the selected item is removed.
        if (dropIndex > selectedIndex)
        {
            dropIndex -= 1;
        }

        object selectedItem = this.listBox1.SelectedItem;

        this.listBox1.Items.Remove(selectedItem);

        if (dropIndex == -1)
        {
            // The item was dropped after the last item so add it at the end of the list.
            this.listBox1.Items.Add(selectedItem);
        }
        else
        {
            // Insert the item above the item it was dropped on.
            this.listBox1.Items.Insert(dropIndex, selectedItem);
        }

        this.listBox1.SelectedItem = selectedItem;
    }
}

private int GetItemIndexAtPoint(Point location)
{
    int itemIndex = -1;

    for (int index = 0; index < this.listBox1.Items.Count; index++)
    {
        // Test whether the location is within an item.
        if (this.listBox1.GetItemRectangle(index).Contains(location))
        {
            itemIndex = index;
            break;
        }
    }

    return itemIndex;
}
4. Run the project and start dragging.

When you drop an item it will be moved to the position above the item you dropped it on. If you drop it after the last item it will be moved to the end of the list.
 
One problem I have, for example is that I have to click the listview item twice before drag and drop to the listbox. first to select the item, second to start draganddrop
The issue there is obvious. When you start dragging, you are assuming that you should simply be able to use the selected item(s) and not putting any thought into it beyond that. If you want to be an effective programmer then you need to be a problem solver, not just a code writer. There are obviously two possible ways that that issue could be resolved. Firstly, the item could become selected when the mouse button is depressed, rather than waiting for the click to be completed with the mouse button being released. That has absolutely nothing specific to do with drag and drop and you're quite capable of researching to determine whether such a thing is possible, either via configuration (setting a property so it happens automatically) or via code. Secondly, you could not just blindly use the selected item(s) and work out what item the mouse was over when you started dragging and use that. You may not be able to come up with a complete solution but you can certainly come up with something that will get you at least part of the way there.
 
please do not post code,
Alas, @jmcilhinney has posted code. @aw48, be sure to skip over posts #4-7 because you don't want to learn from his wisdom and experience. As for other current and future forum readers, let's applaud John for his contributions to helping others learn.
 
Back
Top Bottom