65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (20投票s)

2003 年 4 月 6 日

4分钟阅读

viewsIcon

212688

downloadIcon

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 用来识别某个地方刚刚被拖放了文件(或文件们)的常量。希望这对所有新手都有帮助,因为我花了一周的时间才弄明白这一点,网上关于这个主题的内容并不多,但您会注意到每个大型应用程序都有这个功能。请记住,一旦您知道了被拖放文件的实际路径(它可以是任何文件,而不仅仅是图片),您就可以执行诸如将其保存到数据库、加载到您自己的类文件系统中、或者您想做的任何事情,因为您可以访问用户刚刚执行的操作。

© . All rights reserved.