Introduction
Let's first take a look at how the code has been implemented.
Imports System.Text
Public Class Form1
Inherits System.Windows.Forms.Form
Implements IMessageFilter
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
InitializeComponent()
Application.AddMessageFilter(Me)
DragAcceptFiles(Me.Handle, True)
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Label1 = New System.Windows.Forms.Label()
Me.PictureBox1 = New System.Windows.Forms.PictureBox()
Me.SuspendLayout()
Me.Label1.Font = New System.Drawing.Font("Arial", 9.75!,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.Label1.Location = New System.Drawing.Point(80, 24)
Me.Label1.Name = "Label1"
Me.Label1.Size = New System.Drawing.Size(248, 24)
Me.Label1.TabIndex = 0
Me.Label1.Text = "Drag and Drop Images onto the Form"
Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
Me.PictureBox1.Location = New System.Drawing.Point(104, 88)
Me.PictureBox1.Name = "PictureBox1"
Me.PictureBox1.Size = New System.Drawing.Size(184, 216)
Me.PictureBox1.TabIndex = 1
Me.PictureBox1.TabStop = False
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(440, 357)
Me.Controls.AddRange(New System.Windows.Forms.Control()
{Me.PictureBox1, Me.Label1})
Me.Name = "Form1"
Me.Text = "Drag and Drop Images"
Me.ResumeLayout(False)
End Sub
#End Region
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message)
As Boolean
Implements IMessageFilter.PreFilterMessage
If m.Msg = WM_DROPFILES Then
Dim nfiles As Integer = DragQueryFile(m.WParam, -1, Nothing, 0)
Dim i As Integer
For i = 0 To nfiles
Dim sb As StringBuilder = New StringBuilder(256)
DragQueryFile(m.WParam, i, sb, 256)
HandleDroppedFiles(sb.ToString())
Next
DragFinish(m.WParam)
Return True
End If
Return False
End Function
Public Sub HandleDroppedFiles(ByVal file As String)
If Len(file) > 0 Then
LoadPicture(file)
End If
End Sub
Public Function LoadPicture(ByVal File As String) As Boolean
If Len(File) > 0 Then
Dim b As Bitmap = New Bitmap(File)
picturebox1.SizeMode = PictureBoxSizeMode.StretchImage
picturebox1.Image = b
Return True
End If
Return False
End Function
Private Declare Function DragAcceptFiles Lib "shell32.dll"
(ByVal hwnd As IntPtr, ByVal accept As Boolean) As Long
Private Declare Function DragQueryFile Lib "shell32.dll"
(ByVal hdrop As IntPtr, ByVal ifile As Integer,
ByVal fname As StringBuilder,
ByVal fnsize As Integer) As Integer
Private Declare Sub DragFinish Lib "Shell32.dll" (ByVal hdrop As IntPtr)
Public Const WM_DROPFILES As Integer = 563
End Class
Explanation
Lets look at it line by line.
Imports System.Text
This is because you have to use a String
buffer and there is a class that is perfect that resides in this namespace called StringBuilder
so find the code that says:
Dim sb As StringBuilder = New StringBuilder(256)
This makes 'sb' a String
buffer that can hold 256 characters. This will hold the name of the file that has been dropped onto the Form
.
The next thing to take note of is :
Public Class Form1
Inherits System.Windows.Forms.Form
Implements IMessageFilter
You have to make it so the Form
acts as a message retriever/ sender. The reason is because Windows itself sends messages to your program and your program will send messages to Windows/other applications etc. You have to be able to intercept these messages to truly drag and drop from outside of your application to your application.
So, the guys @ Microsoft made the Interface
, IMessageFilter
to give you a nice cushion from truly using Windows messages like in C or C++. The Interface
, IMessageFilter
only has 1 method called PreMessageFilter
which you HAVE to implement.(When you are implementing an Interface
, you have to)!
So, the next thing to take note of is the one method of that Interface
called PreMessageFilter
which looks like:
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean
Implements IMessageFilter.PreFilterMessage
If m.Msg = WM_DROPFILES Then
Dim nfiles As Integer = DragQueryFile(m.WParam, -1, Nothing, 0)
Dim i As Integer
For i = 0 To nfiles
Dim sb As StringBuilder = New StringBuilder(256)
DragQueryFile(m.WParam, i, sb, 256)
HandleDroppedFiles(sb.ToString())
Next
DragFinish(m.WParam)
Return True
End If
Return False
End Function
This function will file when messages are sent to your application, your job is to find out which message is dropping a file! But luckily, I did that one for you already. The message is 563. 563?!?!?! What the hell is 563... well... it's a Windows constant definition of the message that tells your application a file was dropped on it.. It just has to be that way.
So, if your lost right now, the WM_DROPFILES
= 563. So the if
statement in the above function, if m.msg = WM_DROPFILES
means: If the message that the OS passed to my application = 563 then someone dropped a file on the form. So let's move on.
You can't really just "use" the message filter Interface
, you have to somehow tell your application what exactly is going to act as the message filter. In this case, the Form
itself will act as the message filter and so :-
Public Sub New()
MyBase.New()
InitializeComponent()
Application.AddMessageFilter(Me)
DragAcceptFiles(Me.Handle, True)
End Sub
In this little snippet, we do just that, we add "Me" as the message filter, where the "Me" is the Form
you are currently working with.
Right after that, you must specify to Windows that this handle (this Form
, this application, whatever) will be accepting files from anywhere that is why the call to the API function DRAGACCEPTFILES
is necessary. Windows uses handles instead of names to identify running processes / applications. It's just some big number that uniquely identifies your application so Windows can "handle" it.
Everything up to this point is strictly dealing with how to make the OS and your application deal with sending and receiving messages from the OS / application vice-versa. Now, we have to deal with accepting the actual file and dealing with that. In the PreMessageFilter
function that gets fired every time a message hits the application, it builds a String
buffer from an API call called DragQueryFile
. It is this String
buffer that you will need to tell any other piece of your application that "that file is currently being dragged onto your form" the declaration for this API call is:
Private Declare Function DragQueryFile Lib "shell32.dll"
(ByVal hdrop As IntPtr, ByVal ifile As Integer, ByVal fname As StringBuilder,
ByVal fnsize As Integer) As Integer
So after that API call fills the String
buffer (StringBuilder
/ buffer sheesh!) you can handle what happens next. So in the function HandleDroppedFiles()
is where to do this one. This is a user function, so this can be anything I want it to be, my 2 functions look like:
Public Sub HandleDroppedFiles(ByVal file As String)
If Len(file) > 0 Then
LoadPicture(file)
End If
End Sub
Public Function LoadPicture(ByVal File As String) As Boolean
If Len(File) > 0 Then
Dim b As Bitmap = New Bitmap(File)
picturebox1.SizeMode = PictureBoxSizeMode.StretchImage
picturebox1.Image = b
Return True
End If
Return False
End Function
All this does is take the String
buffer (after it has been filled and is actually a real String
. (I'm a real boy)), as a parameter and it should look like "C:\file.jpg" or whatever, you can then proceed to tell your component/ control/ Form
whatever, that its image is going to be that one. So to sum up my first ever tutorial you need these API declarations to successfully use dragging and dropping from outside of your application:
Private Declare Function DragAcceptFiles Lib "shell32.dll"
(ByVal hwnd As IntPtr, ByVal accept As Boolean) As Long
Private Declare Function DragQueryFile Lib "shell32.dll"
(ByVal hdrop As IntPtr, ByVal ifile As Integer, ByVal fname As StringBuilder,
ByVal fnsize As Integer) As Integer
Private Declare Sub DragFinish Lib "Shell32.dll" (ByVal hdrop As IntPtr)
Public Const WM_DROPFILES As Integer = 563
Remember that the WM_DROPFILES
= 563 is just the constant that Windows uses to realize that there is a file(s) that just got dropped somewhere. Hope this helps all of you newbies out there because it took me a freaking week to get this one down, there's not much on this topic out on the net, but you notice every major application has this functionality. Keep in mind, that once you know the actual path of the file dropped (it could be any file and not just images), you can then do things like, save it to a database, load it into your own quazi-file system, whatever the hell you want to do because you have access to exactly what the user just did.