Drag&Drop files to a listbox hangs explorer and the program...

JumpyNET

Centurion
Joined
Apr 4, 2005
Messages
196
I have a form with one ListBox in it and use the code below. I have allowed drop to the listbox and plan to drop files to it (both folders and files). Files are added at once and folders are searched for more files and subfolders. But when I add a folder containing alot of files like the C-drive then both the program and explorer hang until all files are added.
I could prevent the program from hanging by adding Application.DoEvents() here and there but of course it has no effect on explorer. So how can I prevent explorer from hanging? I would like to drag&drop other folders to my app from explorer even when the previous folder had not yeat been fully processed.

Visual Basic:
    Private Sub ListBox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter

        ' Let's allow files to be dropped.
        If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
            e.Effect = DragDropEffects.Copy
        Else
            e.Effect = DragDropEffects.None
        End If

    End Sub

    Private Sub ListBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop

        ' Accept on files and folders.
        If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
            Dim Items() As Object = CType(e.Data.GetData(DataFormats.FileDrop), System.Object)
            Dim File As Object
            For Each File In Items
                ' Let's see if we are dealing with a file or a folder.
                If System.IO.File.Exists(File) Then
                    ' It's a file, so we add it to the listbox.
                    AddFile(File.ToString)
                ElseIf System.IO.Directory.Exists(File) Then
                    ' It's a folder, so we must search it for files.
                    FindFiles(File.ToString)
                End If
            Next
        End If

    End Sub

    Private Sub AddFile(ByVal FilePath As String)

        ' Simply add the file to the listbox.
        ListBox1.Items.Add(FilePath)

    End Sub

    Private Sub FindFiles(ByVal Directory As String)

        ' Find files in current folder.
        Try
            Dim Files As String() = System.IO.Directory.GetFiles(Directory, "*")
            Dim File As String
            For Each File In Files
                AddFile(File)
            Next
        Catch
        End Try

        ' Find subfolders in current folder and search them for files too.
        Try
            Dim Dirs As String() = System.IO.Directory.GetDirectories(Directory, "*")
            Dim Dir As String
            For Each Dir In Dirs
                FindFiles(Dir)
            Next
        Catch
        End Try

    End Sub
 
The ThreadPool object allows starting a separate thread with a parameter, using the QueueUserWorkItem method, the 2nd overload has two parameters: a WaitCallback delegate (the method to be called on a separate thread), and an object that will be passed to this method. This is a different solution than the one in that post, but there is a decent amount of explanation on msdn for it ;).

Search this forum for InvokeRequired for how to access user controls from a different thread. It should return plenty of results ;).
 
I came up with this. Do you think there's any bad practise in the code?

Visual Basic:
Imports System.Threading

Public Class Form1




    Private Sub ListBox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter

        ' Let's allow files to be dropped.
        If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
            e.Effect = DragDropEffects.Copy
        Else
            e.Effect = DragDropEffects.None
        End If

    End Sub

    Private Sub ListBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop

        ' Accept files and folders.
        If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then

            Dim Items() As Object = CType(e.Data.GetData(DataFormats.FileDrop), System.Object)

            Dim AddFilesClass As New AddFiles
            AddFilesClass.AddItems(Items, ListBox1)

        End If

    End Sub




End Class

Public Class AddFiles

    Private Shared ThreadCount As Integer

    Private Delegate Sub AddItemDelegate(ByVal FullPath As String, ByVal ListBox As ListBox)
    Private Delegate Sub StartAddItemsDelegate(ByVal Items() As Object, ByVal ListBox As ListBox)
    Private Delegate Sub EndAddItemsDelegate(ByVal ThreadName As String)

    Private Class AddFileArgs

        Public Items() As Object
        Public ListBox As ListBox
        Public StartDelegate As StartAddItemsDelegate
        Public EndDelegate As EndAddItemsDelegate

        Public Sub StartAdding()

            StartDelegate(Items, ListBox)
            EndDelegate(Thread.CurrentThread.Name)

        End Sub

    End Class

    Private Sub FilesAndFolders(ByVal Items() As Object, ByVal ListBox As ListBox)

        Dim File As Object
        For Each File In Items
            ' Let's see if we are dealing with a file or a folder.
            If System.IO.File.Exists(File) Then
                ' It's a file, so we add it to the listbox.
                If ListBox.InvokeRequired Then
                    ListBox.Invoke(New AddItemDelegate(AddressOf AddFile), New Object() {File.ToString, ListBox})
                Else
                    Me.AddFile(File.ToString, ListBox)
                End If
            ElseIf System.IO.Directory.Exists(File) Then
                ' It's a folder, so we must search it for files.
                AddDirectory(File.ToString, ListBox)
            End If
        Next

    End Sub

    Private Sub AddFile(ByVal FilePath As String, ByVal ListBox As ListBox)

        ' Simply add the file to the listbox.
        ListBox.Items.Add(FilePath)

    End Sub

    Private Sub AddDirectory(ByVal Directory As String, ByVal ListBox As ListBox)

        ' Find files in current folder.
        Try
            Dim Files As String() = System.IO.Directory.GetFiles(Directory, "*")
            Dim File As String
            For Each File In Files
                ' Add the file
                If ListBox.InvokeRequired Then
                    ListBox.Invoke(New AddItemDelegate(AddressOf AddFile), New Object() {File, ListBox})
                Else
                    Me.AddFile(File, ListBox)
                End If
            Next
        Catch
        End Try

        ' Find subfolders in current folder and search them for files too.
        Try
            Dim Dirs As String() = System.IO.Directory.GetDirectories(Directory, "*")
            Dim Dir As String
            For Each Dir In Dirs
                AddDirectory(Dir, ListBox)
            Next
        Catch
        End Try

    End Sub

    Private Sub Done(ByVal threadName As String)

        'If ListBox1.InvokeRequired Then
        '    Debug.Print("Done is executing on the launched thread.")
        'Else
        '    Debug.Print("Done is executing on the main thread.")
        'End If
        Debug.Print(threadName & " finished.")

    End Sub

    ' *** It all starts here ****
    Public Sub AddItems(ByVal Items() As Object, ByVal ListBox As ListBox)

        Dim ca As New AddFiles.AddFileArgs
        ca.Items = Items
        ca.ListBox = ListBox
        ca.StartDelegate = AddressOf FilesAndFolders
        ca.EndDelegate = AddressOf Done

        Dim t As New Thread(AddressOf ca.StartAdding)
        ThreadCount += 1
        t.Name = "Thread " & ThreadCount
        Debug.Print(t.Name & " started.")
        t.Priority = ThreadPriority.BelowNormal
        t.IsBackground = True
        t.Start()

    End Sub

End Class
 
Last edited:
Back
Top