Drag and Drop Between More Than 2 Listboxes

rickb

Freshman
Joined
Dec 31, 2002
Messages
26
Location
St. Louis, Missouri
I need to be able to move listbox items around a collection of up to 12 listboxes. For example, I move an item from listbox1 to listbox2; later, I need to move that same item from listbox2 to listbox3, and then on a future date move the item from listbox3 back to listbox1. I also need the item to be removed from its previous location when it moves to a new listbox.

Here is the code I have for moving items between 2 listboxes:

Code:
Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
        If ListBox1.SelectedIndex < 0 Then Return
        ListBox1.DoDragDrop(ListBox1.Items(ListBox1.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    End Sub

    Private Sub listbox2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox2.MouseDown
        If ListBox2.SelectedIndex < 0 Then Return
        ListBox2.DoDragDrop(ListBox2.Items(ListBox2.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    End Sub

    Private Sub listbox2_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragEnter
        e.Effect = DragDropEffects.All
    End Sub

    Private Sub listbox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter
        e.Effect = DragDropEffects.All
    End Sub

    Private Sub listbox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop
        If ListBox2.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
            ListBox2.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
            ListBox1.Items.Remove(ListBox1.Items(ListBox1.SelectedIndex))
        End If
    End Sub

    Private Sub listbox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop
        If ListBox1.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
            ListBox1.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
            ListBox2.Items.Remove(ListBox2.Items(ListBox2.SelectedIndex))
        End If
    End Sub

This is not original code from me; I got it from another site.

I modified this code to include a third listbox, but the modification doesn't remove the item from the its previous listbox (I added the listbox3 "Remove" code to the original DragDrop events and duplicated the MouseDown and DragEnter events); it copies it to the new listbox.

I appreciate any help or guidance.
 
You should probably post the actual code you are using so it is easier to see where the problem is. I don't see any references to ListBox3 in what you've got there.
 
Sorry about that.
I solved the lesser problem of doing more than 2 listboxes (it works with 3 listboxes), so I'll post that code. However, this doesn't resolve the larger issue where I need to be able to swap items between 12 listboxes because this code would get ridiculously long.

Here is the code:
Code:
Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
        If ListBox1.SelectedIndex < 0 Then Return
        ListBox1.DoDragDrop(ListBox1.Items(ListBox1.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    End Sub

    Private Sub listbox2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox2.MouseDown
        If ListBox2.SelectedIndex < 0 Then Return
        ListBox2.DoDragDrop(ListBox2.Items(ListBox2.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    End Sub
    Private Sub listbox3_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox3.MouseDown
        If ListBox3.SelectedIndex < 0 Then Return
        ListBox3.DoDragDrop(ListBox3.Items(ListBox3.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    End Sub

    'These subs allow entrance into the selected listbox

    Private Sub listbox2_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragEnter
        e.Effect = DragDropEffects.All
    End Sub

    Private Sub listbox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter
        e.Effect = DragDropEffects.All
    End Sub
    Private Sub listbox3_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox3.DragEnter
        e.Effect = DragDropEffects.All
    End Sub

    'These subs allows the cursor to enter the listboxes and drop items into them

    Private Sub listbox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop
        If ListBox2.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
            ListBox2.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
            If ListBox1.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) <> -1 Then
                ListBox1.Items.Remove(ListBox1.Items(ListBox1.SelectedIndex))
            Else
                ListBox3.Items.Remove(ListBox3.Items(ListBox3.SelectedIndex))
            End If
        End If
    End Sub
    Private Sub listbox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop

        If ListBox1.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
            ListBox1.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
            If ListBox2.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) <> -1 Then
                ListBox2.Items.Remove(ListBox2.Items(ListBox2.SelectedIndex))
            Else
                ListBox3.Items.Remove(ListBox3.Items(ListBox3.SelectedIndex))
            End If
        End If
    End Sub
    Private Sub listbox3_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox3.DragDrop
        If ListBox3.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
            ListBox3.Items.Add(e.Data.GetData(DataFormats.Text).ToString)

            If ListBox1.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) <> -1 Then
                ListBox1.Items.Remove(ListBox1.Items(ListBox1.SelectedIndex))
            Else
                ListBox2.Items.Remove(ListBox2.Items(ListBox2.SelectedIndex))
            End If
        End If
    End Sub

I would like to be able to work through the different listboxes with a SELECT CASE statement by referencing each lisbox, but I'm not sure how I would do that.
Thanks for any help or guidance.
 
When the drag operation starts, you can store a reference to the control that started the operation. That way you don't have to go looking for it later on.
Code:
Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
    If ListBox1.SelectedIndex < 0 Then Return
    ListBox1.DoDragDrop(ListBox1.Items(ListBox1.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
End Sub
becomes...
Code:
[COLOR="Blue"]Dim dragSource As ListBox[/COLOR]

Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
    If ListBox1.SelectedIndex < 0 Then Return
    ListBox1.DoDragDrop(ListBox1.Items(ListBox1.SelectedIndex).ToString, DragDropEffects.Copy Or DragDropEffects.Move)
    [COLOR="Green"]'Remember what control the data came from.[/COLOR]
    [COLOR="Blue"]dragSource = ListBox1[/COLOR]
End Sub
But why not generalize it a bit more?
Code:
[COLOR="Blue"]Dim dragSource As ListBox[/COLOR]

Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
    [COLOR="Blue"]TryBeginDrag(ListBox1)[/COLOR]
End Sub

[COLOR="Green"]'We can use this sub for any ListBox we want to drag data from[/COLOR]
[COLOR="Blue"]Private Sub TryBeginDrag(source As ListBox)
    If source.SelectedIndex >= 0 Then
        source.DoDragDrop(source.SelectedItem.ToString, DragDropEffects.Copy Or DragDropEffects.Move)    
        dragSource = source
    End If
End Sub[/COLOR]


Now when we drag and drop to another control, we already know exactly where to remove the string from.
Code:
Private Sub listbox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop
    If ListBox2.FindStringExact(e.Data.GetData(DataFormats.Text).ToString) = -1 Then
        ListBox2.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
[COLOR="Blue"]        dragSource.Items.Remove(dragSource.SelectedItem)[/COLOR]
    End If
End Sub
Again, we can generalize:
Code:
Private Sub listbox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop
[COLOR="Blue"]    TryDrop(ListBox2, e.Data)[/COLOR]
End Sub

[COLOR="Blue"]
Private Sub TryDrop(destination As ListBox, data As IDataObject)
[COLOR="Green"]    'I find it easier to read my code if I evaluate a large expression outside
    'the if statement and give it a name.[/COLOR]
    Dim destContainsData As Boolean = destination.FindStringExact(data.GetData(DataFormats.Text).ToString()) <> -1

    If Not destContainsData Then
        destination.Items.Add(Data.GetData(DataFormats.Text).ToString)
        dragSource.Items.Remove(dragSource.SelectedItem)
    End If
End Sub
[/COLOR]

I haven't tested this code and my VB is a bit rusty, but at the very least you should get the idea. Hope it helps.
 
Last edited:
Thanks for the help, marble-eater.

I'm getting an error in the TryDrop sub. The code is below and the error is at the arrow:

Code:
Private Sub TryDrop(ByVal destination As ListBox, ByVal data As IDataObject)
        
        Dim destContainsData As Boolean =  destination.FindStringExact(data.GetData(DataFormats.Text).ToString()) <> -1

        If Not destContainsData Then
 -->        [B]destination.Items.Add(e.Data.GetData(DataFormats.Text).ToString)[/B]
            dragSource.Items.Remove(dragSource.SelectedItem)
        End If

End Sub

Here is the error msg: "Name 'e' is not declared."

I tried copying "ByVal e As System.Windows.Forms.DragEventArgs" into the sub declaration, but I got this error msg: "Argument not specified." I also tried to put "ByVal e As System.Windows.Forms.MouseEventArgs", but I got the "Not a member" error msg. I even took the 'e' out; that eliminated the error msg, but the program still did not work properly.

Out of curiosity, I answered "Yes" once when asked if I wanted to continue in spite of the error. The program seems to work AFTER the first time I move one item from one listbox to another, but only with the items that have not been moved yet.

In case you are wondering, I copied your examples and modified it to work for all the listboxes. Here is the entire code with your modifications:

Code:
'We can use this sub for any ListBox we want to drag data from
    Private Sub TryBeginDrag(ByVal source As ListBox)
        If source.SelectedIndex > 0 Then
            source.DoDragDrop(source.SelectedItem.ToString, DragDropEffects.Copy Or DragDropEffects.Move)
            dragsource = source
        End If
    End Sub

    Private Sub TryDrop(ByVal destination As ListBox, ByVal data As IDataObject)
        'I find it easier to read my code if I evaluate a large expression outside
        'the if statement and give it a name.
        Dim destContainsData As Boolean = destination.FindStringExact(data.GetData(DataFormats.Text).ToString()) <> -1

        If Not destContainsData Then
-->            destination.Items.Add(e.Data.GetData(DataFormats.Text).ToString)
            dragsource.Items.Remove(dragsource.SelectedItem)
        End If
    End Sub


    '===============================================================================================================================================
    '===============================================================================================================================================

    Private Sub listbox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDown
        TryBeginDrag(ListBox1)
    End Sub
    Private Sub listbox2_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox2.MouseDown
        TryBeginDrag(ListBox2)
    End Sub
    Private Sub listbox3_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox3.MouseDown
        TryBeginDrag(ListBox3)
    End Sub

    Private Sub listbox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop
        TryDrop(ListBox1, e.Data)
    End Sub
    Private Sub listbox2_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragDrop
        TryDrop(ListBox2, e.Data)
    End Sub
    Private Sub listbox3_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox3.DragDrop
        TryDrop(ListBox3, e.Data)
    End Sub


    Private Sub listbox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter
        e.Effect = DragDropEffects.All
    End Sub
    Private Sub listbox2_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox2.DragEnter
        e.Effect = DragDropEffects.All
    End Sub
    Private Sub listbox3_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox3.DragEnter
        e.Effect = DragDropEffects.All
    End Sub

Thanks again for any help you can give me.
 
My mistake.


Code:
        destination.Items.Add([COLOR="Red"]e.[/COLOR]Data.GetData(DataFormats.Text).ToString)
Should be
Code:
        destination.Items.Add(Data.GetData(DataFormats.Text).ToString)
 
Thanks for the reply.

I know my last post was a long one, but somewhere in there I mentioned that I already removed the 'e.' The error goes away, but the program doesn't work properly until after the first time I move an object, and then seemingly stops allowing DragEnter events (You can't move anything into the listboxes; it's as if the AllowDrop property was switched to 'False'). I included the error part in the post with the hope that you saw something you missed.

I do appreciate the time you've put into this issue; I'm going to play with it a while and see if I can narrow down what the problem is. I would gladly accept anymore ideas/suggestions you have.

Thanks again.
 
Sorry, I guess I didn't read it well enough. Here is what seems to be the problem: I didn't realize that the Control.DoDragDrop method blocks the main thread until the drag and drop operation is complete. This is the corrected code for TryBeginDrag:
Code:
    Private Sub TryBeginDrag(ByVal source As ListBox)
        If source.SelectedIndex >= 0 Then
            [COLOR="Red"]dragSource = source[/COLOR] [COLOR="Green"]' moved[/COLOR] 
[COLOR="Green"]            ' This method blocks the main thread[/COLOR] 
           source.DoDragDrop(source.SelectedItem.ToString, DragDropEffects.Copy Or DragDropEffects.Move)
        End If
    End Sub
dragSource needs to be set before calling DoDragDrop. Otherwise it won't get set until after the DragEnter and DragDrop events are raised. I guess I didn't realize that it worked that way because I'm not familiar with DotNet's drag and drop system. I don't use it because it just seems aweful (probably because it just wraps the Windows drag and drop system, which is, presumably, aweful).
 
Excellent, marble-eater! Thanks for figuring that out. I played around with the code for a couple of hours today, but nothing seemed to work.

Thanks again for your effort.
 
Back
Top