[RESOLVED] Multiple file move via DragAndDrop
I'm going to try this again since last time I asked (a long time ago now) I didn't get many responses. Maybe someone who knew didn't see my post or maybe someone new is around who might know.
I want to add a file drag and drop feature to my program that allows people to drag files onto my form and when the files are released on a path (that is stored in a listbox) it moves/copies the files to that folder.
I have all that working but what my issue is that each file has to be handled individually.
Now, Windows can move multiple files all in a single group; it calculates time till completion based on the cumulative size of all the files and shows a progress bar in that way.
Currently, all I can do is either let it happen, or show a progress bar for each file.
Does anyone know how to get files in an array of files to drop to display like Windows does with multiple files when moving or copying.
Re: Multiple file move via DragAndDrop
You're not having problems getting your form to process multiple dragged files right? What you want is an accurate multifile copy progress bar. In that case, you'll want manual copying. Use filestreams with a progressbar. Something like this:
Code:
Public Class Form1
Friend WithEvents bgCopy As New System.ComponentModel.BackgroundWorker With {.WorkerReportsProgress = True}
Private Structure copyDetails
Dim moveFiles As Boolean
Dim destination As String
Dim filesToCopy() As IO.FileInfo
End Structure
Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click
Dim files(ListBox1.Items.Count - 1) As IO.FileInfo
Dim total As Long = 0
For i As Integer = 0 To ListBox1.Items.Count - 1
files(i) = New IO.FileInfo(ListBox1.Items(i).ToString())
total += files(i).Length
Next
ProgressBar1.Maximum = total
ProgressBar1.Step = 4096
bgCopy.RunWorkerAsync( _
New copyDetails() With { _
.destination = TextBox1.Text, _
.filesToCopy = files, _
.moveFiles = CheckBox1.Checked _
} _
)
End Sub
Private Sub bgCopy_DoWork( _
ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs _
) Handles bgCopy.DoWork
Dim copyArgs As copyDetails = DirectCast(e.Argument, copyDetails)
Dim bgWorker As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
For Each f As IO.FileInfo In copyArgs.filesToCopy
Using fsIn As New IO.FileStream( _
f.FullName, IO.FileMode.Open, IO.FileAccess.Read _
)
Using fsOut As New IO.FileStream( _
f.FullName.Replace(f.DirectoryName, copyArgs.destination), IO.FileMode.Create, IO.FileAccess.Write _
)
Dim offset As Long = 0
Dim total As Long = fsIn.Length
Dim toCopy(4096) As Byte
While offset + 4096 < total
fsIn.Read(toCopy, 0, toCopy.Length)
fsOut.Write(toCopy, 0, toCopy.Length)
bgWorker.ReportProgress(0)
offset += 4096
End While
fsIn.Read(toCopy, 0, toCopy.Length)
fsOut.Write(toCopy, 0, CInt(total - offset))
bgWorker.ReportProgress(0)
fsIn.Flush()
fsOut.Flush()
End Using
End Using
If copyArgs.moveFiles Then _
f.Delete()
Next
End Sub
Private Sub bgCopy_ProgressChanged( _
ByVal sender As System.Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs _
) Handles bgCopy.ProgressChanged
If ProgressBar1.Value < ProgressBar1.Maximum - ProgressBar1.Step Then
ProgressBar1.PerformStep()
Else
ProgressBar1.Value = ProgressBar1.Maximum
End If
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Using f As New OpenFileDialog() With {.Multiselect = True}
f.ShowDialog()
ListBox1.Items.AddRange(f.FileNames)
End Using
End Sub
End Class
This may not be the most efficient method, we'll have to get input from others. I used a background worker for obvious reasons.
Re: Multiple file move via DragAndDrop
Yeah. No problems with actually moving the files. Just looking for a way to contain all the files and make it look like Windows is handling them as a whole, like Windows does.
I just wasn't sure if there was a way to do it or if I'd have to build my own setup.
I'll probably fool around with this later when I get home and see how it works.
Thanks for the input :)
Re: Multiple file move via DragAndDrop
OK, so here's my modifications.
Your base functions still exist as you wrote them, but I had to modify what was in the Button1 to try to get it to work with my DragDrop event.
I added: "Friend WithEvents bgCopy As New System.ComponentModel.BackgroundWorker With {.WorkerReportsProgress = True}" and got the following error:
Error 1 'bgCopy' is already declared as 'Friend WithEvents bgCopy As System.ComponentModel.BackgroundWorker' in this class. D:\_prog\SysPad\SysPad\frmFolderPad.vb 19 23 SysPad
And as far as running the code, nothing happens when I drop some files on the list item.
Code:
Private Sub folderList_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles folderList.DragDrop
If e.Data.GetDataPresent("FileNameW", True) Then
Dim paths As System.Array = CType(e.Data.GetData(DataFormats.FileDrop), System.Array)
Dim strDestPath As String = txtPath.Text
Dim blnMove As Boolean
Dim totalFiles As Integer = paths.Length
If (e.KeyState And CtrlMask) = CtrlMask Then
blnMove = False
Else
blnMove = True
End If
Dim files(totalFiles) As IO.FileInfo
Dim total As Long = 0
For i As Integer = 0 To totalFiles
files(i) = New IO.FileInfo(paths(i).ToString)
total += files(i).Length
Next
ProgressBar1.Maximum = totalFiles
ProgressBar1.Step = 4096
bgCopy.RunWorkerAsync(New copyDetails() With {.destination = strDestPath & "\", .filesToCopy = files, .moveFiles = blnMove})
End If
End Sub
Re: Multiple file move via DragAndDrop
/bump
If anyone else can makes sense of this and maybe provide some insight, that would be greatly appreciated.
This feature has yet to make it into a version of my program, but would really like to try to implement it in this next update.
I tried awhile back, and got the basic concept working, but didn't work like I'm trying to accomplish here.
Re: Multiple file move via DragAndDrop
It's not working because your first if statement is returning false. What is "FileNameW"?
Re: Multiple file move via DragAndDrop
It has something to do with GetDataPresent directly as in, you could put "FileName" and it would return something different.
Not sure what you call it.
Maybe I deleted some other code; though I thought I still had it all, just commented out till I needed it again.
Re: Multiple file move via DragAndDrop
.fileToCopy is just suppsoed to be an array of file paths right?
I know that files is populated with my paths because I ran a MessageBox outputting whatever was in the files(i) index after each index was given its value.
If files is simply an array of string paths, then it should be being passed fine to the bgCopy.
Re: Multiple file move via DragAndDrop
Put a breakpoint anywhere in that code and see if it gets tripped, then you'll know if the code is even executing at all.
Re: Multiple file move via DragAndDrop
Well, no MessageBoxes (I prefer message boxes) displayed from within the DoWork.
I even took the If statement out and the RunWorkerAsync still didn't trigger.
Re: Multiple file move via DragAndDrop
E.g.
Code:
Public Class Form1
Private Sub Form1_DragEnter(ByVal sender As Object, _
ByVal e As DragEventArgs) Handles Me.DragEnter
If Me.CanCopyFiles(e) Then
e.Effect = DragDropEffects.Copy
End If
End Sub
Private Sub Form1_DragDrop(ByVal sender As Object, _
ByVal e As DragEventArgs) Handles Me.DragDrop
If Me.CanCopyFiles(e) Then
Me.BackgroundWorker1.RunWorkerAsync(DirectCast(e.Data.GetData("FileDrop"), String()))
End If
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim filePaths = DirectCast(e.Argument, String())
Dim totalSize = 0L
For Each filePath In filePaths
totalSize += New IO.FileInfo(filePath).Length
Next
Dim destinationPath = IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "Test")
If Not IO.Directory.Exists(destinationPath) Then
IO.Directory.CreateDirectory(destinationPath)
End If
Const BUFFER_SIZE As Integer = 4096
Dim buffer(BUFFER_SIZE - 1) As Byte
Dim bytesRead As Integer
Dim bytesCopied = 0L
For Each filePath In filePaths
Using source = IO.File.Open(filePath, _
IO.FileMode.Open)
Using destination = IO.File.Open(IO.Path.Combine(destinationPath, _
IO.Path.GetFileName(filePath)), _
IO.FileMode.Create)
Do
bytesRead = source.Read(buffer, 0, BUFFER_SIZE)
If bytesRead > 0 Then
destination.Write(buffer, 0, bytesRead)
bytesCopied += bytesRead
Me.BackgroundWorker1.ReportProgress(Me.GetPercentage(bytesCopied, _
totalSize), _
filePath)
End If
Loop Until bytesRead < BUFFER_SIZE
End Using
End Using
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.Label1.Text = CStr(e.UserState)
Me.ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
MessageBox.Show("Copy complete")
End Sub
Private Function CanCopyFiles(ByVal e As DragEventArgs) As Boolean
Return e.Data.GetDataPresent("FileDrop") AndAlso _
(e.AllowedEffect And DragDropEffects.Copy) = DragDropEffects.Copy
End Function
Private Function GetPercentage(ByVal value As Long, ByVal total As Long) As Integer
Return CInt(value / total * 100L)
End Function
End Class
Assumes that the AllowDrop property of the form and the WorkerReportsProgress property of the BackgroundWorker are both True. Note that the Maximum of the ProgressBar should also be left at the default of 100 as we are reporting a percentage of data copied, not an absolute number of bytes.
Finally, not that I use "FileDrop" as the data format. "FileName" and "FileNameW" can only get one file path while "FileDrop" can get multiple.
Re: Multiple file move via DragAndDrop
AllowDrop is actually set on the listbox folderList (for my case)
WorkerReportsProgress was not True (is now)
FileNameW can get more than one.
I ran it through a loop. All files that were dropped were listed with their paths.
Re: Multiple file move via DragAndDrop
Dunno if those were supposed to help, but nothing is happening still.
No messageboxes.
Re: Multiple file move via DragAndDrop
My code copies files as is. Have you tried it? The idea is that you would create a new project and then run my code to see how it works. I would think that you should be able to adapt that to your own needs. It's an example that demonstrates the principle and you can then implement the principle as appropriate in your own project.
Re: Multiple file move via DragAndDrop
oh..hah...lol...i was thinkin u were commenting on his code..sorry...wanst paying attention
Re: Multiple file move via DragAndDrop
Well, that worked perfectly fine.
However, folders won't move.
The program crashes with an error stating the "file" can't be found.
Do folder's have to be handled differently?
Re: Multiple file move via DragAndDrop
Folders aren't files so yes, they have to be handled differently. You can call File.Exists and, if that's False, Directory.Exists to decide whether the path refers to a file or folder. You can then use the File class or the Directory class to process it accordingly.
Re: Multiple file move via DragAndDrop
Re: Multiple file move via DragAndDrop
well...im looking at this, and I'm not exactly sure what to do...
I mean, once I determine if it's a folder...how do I determine the size of what's in it.
I mean, I guess I can move it with .Move, but it seems to be able to determine exactly what is in there for the total size for the progress bar, i need to go through each folder and determine all of the files that are in them.
and what about moving a folder. If I use .Move, will it move the progressbar properly while the folder is being moved?
Re: Multiple file move via DragAndDrop
To determine the size of a folder you simply have to sum the sizes of the files it contains. Try viewing the properties of a large folder in Windows Explorer and you'll see Windows does the same.
How exactly would calling Move update the ProgressBar? We can't even call File.Move so we can hardly call Directory.Move. To move a folder you'll have to move each file it contains in the same way as you already are moving files. To move a folder would involve creating the new folder, copying the files, deleting the old files and deleting the old folder.