为 VB.NET 控件添加拖放支持






4.40/5 (20投票s)
2003 年 4 月 6 日
4分钟阅读

212688

1107
解释如何为 VB.NET 控件添加拖放支持的文章
引言
首先,让我们看看代码是如何实现的。
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()
'
'Label1
'
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
'
'PictureBox1
'
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
'
'Form1
'
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
'this code to handle multiple dropped files..
'not really neccesary for this example
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
解释
逐行来看。
Imports System.Text
这是因为您必须使用一个 String
缓冲区,并且该命名空间中有一个非常适合的类,名为 StringBuilder
,所以找到显示的代码:
Dim sb As StringBuilder = New StringBuilder(256)
这使得 'sb' 成为一个可以容纳 256 个字符的 String
缓冲区。这将保存拖放到 Form
上的文件名。
接下来需要注意的一点是:
Public Class Form1
Inherits System.Windows.Forms.Form
Implements IMessageFilter
您必须让 Form
充当消息检索器/发送器。原因是 Windows 本身会向您的程序发送消息,而您的程序会向 Windows/其他应用程序等发送消息。您必须能够拦截这些消息,才能真正地将外部应用程序拖放到您的应用程序中。
因此,Microsoft 的开发人员创建了 Interface
IMessageFilter
,让您免去了像 C 或 C++ 那样真正使用 Windows 消息的麻烦。 Interface
IMessageFilter
只有一个名为 PreMessageFilter
的方法,您必须实现它(当您实现 Interface
时,您必须这样做!)。
因此,接下来需要注意的就是该 Interface
的一个方法,名为 PreMessageFilter
,它看起来像这样:
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean
Implements IMessageFilter.PreFilterMessage
If m.Msg = WM_DROPFILES Then
'this code to handle multiple dropped files..
'not really neccesary for this example
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
当消息发送到您的应用程序时,此函数将执行。您的任务是找出哪个消息正在拖放文件!但幸运的是,我已经为您完成了这项工作。消息是 563。563?!?? 563 是什么鬼……嗯……它是 Windows 中一个常量定义,告诉您的应用程序有文件被拖放到上面了。它就是这样。
所以,如果您现在感到困惑,WM_DROPFILES
= 563。因此,上述函数中的 if
语句,if m.msg = WM_DROPFILES
的意思是:如果操作系统传递给我的应用程序的消息 = 563,那么有人将文件拖到了窗体上。所以,让我们继续。
您不能真正地“使用”消息过滤器 Interface
,您必须以某种方式告诉您的应用程序,具体哪个部分将充当消息过滤器。在这种情况下,Form
本身将充当消息过滤器,所以:
Public Sub New()
MyBase.New()
InitializeComponent()
Application.AddMessageFilter(Me)
DragAcceptFiles(Me.Handle, True)
End Sub
在这个小代码片段中,我们正是这样做的,我们将“Me”添加为消息过滤器,“Me”是指您当前正在处理的 Form
。
在此之后,您必须向 Windows 指定此句柄(此 Form
,此应用程序,无论是什么)将接受来自任何地方的文件,这就是为什么调用 API 函数 DRAGACCEPTFILES
是必要的。Windows 使用句柄而不是名称来标识正在运行的进程/应用程序。它只是一个唯一标识您的应用程序的大数字,以便 Windows 可以“处理”它。
到目前为止,所有内容都严格涉及如何让操作系统和您的应用程序处理与操作系统/应用程序之间发送和接收消息。现在,我们必须处理实际接受文件并处理它的问题。在每次消息到达应用程序时都会触发的 PreMessageFilter
函数中,它会通过一个名为 DragQueryFile
的 API 调用构建一个 String
缓冲区。正是这个 String
缓冲区,您需要告知您应用程序的其他任何部分“该文件当前正被拖放到您的窗体上”,此 API 调用的声明是:
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
因此,在调用该 API 调用填充 String
缓冲区(StringBuilder
/缓冲区,真是的!)之后,您可以处理接下来发生的事情。因此,在 HandleDroppedFiles()
函数中是执行此操作的地方。这是一个用户函数,因此它可以是任何我想要的东西,我的两个函数看起来像:
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
所有这些只是将 String
缓冲区(在它被填充并且实际上是一个真正的 String
之后(我是一个真正的男孩!))作为参数,它应该看起来像“C:\file.jpg”或者任何其他文件名。然后,您可以继续告诉您的组件/控件/Form
等,它的图像将是那个文件。因此,总结我的第一个教程,您需要这些 API 声明才能成功地将外部应用程序拖放到您的应用程序中:
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
请记住,WM_DROPFILES
= 563 只是 Windows 用来识别某个地方刚刚被拖放了文件(或文件们)的常量。希望这对所有新手都有帮助,因为我花了一周的时间才弄明白这一点,网上关于这个主题的内容并不多,但您会注意到每个大型应用程序都有这个功能。请记住,一旦您知道了被拖放文件的实际路径(它可以是任何文件,而不仅仅是图片),您就可以执行诸如将其保存到数据库、加载到您自己的类文件系统中、或者您想做的任何事情,因为您可以访问用户刚刚执行的操作。