问题
I'm working on a form that has ten picture boxes. Children should be able to drag a picture from one picture box to any of the others. I read an excellent Microsoft article that explained the steps in setting up a drag/drop between two picture boxes. There were five sub routines to drag and drop one way into a second picture box.
My concern is that if the form has ten picture boxes, and the user can drag/drop between any of the ten boxes, that is a lot of code and and a lot of sub routines.
Is there a more elegant way to do this? I've put the code below for utilizing drag/drop between two picture boxes. I'm using Visual Basic 2010 Express. Thanks.
' Enable dropping.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
PictureBox2.AllowDrop = True
End Sub
' Set a flag to show that the mouse is down for PictureBox1
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
If Not PictureBox1.Image Is Nothing Then
MouseIsDown = True
End If
End Sub
' Initiate dragging and allow either copy or move for PictureBox1
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If MouseIsDown Then
PictureBox1.DoDragDrop(PictureBox1.Image, DragDropEffects.Copy Or _
DragDropEffects.Move)
End If
MouseIsDown = False
End Sub
'Copy or Move data from PictureBox1
Private Sub PictureBox1_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox1.DragEnter
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
' Check for the CTRL key.
If e.KeyState = 9 Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
' Assign the image to the PictureBox2
Private Sub PictureBox2_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox2.DragDrop
PictureBox2.Image = e.Data.GetData(DataFormats.Bitmap)
' If the CTRL key is not pressed, delete the source picture.
If Not e.KeyState = 8 Then
PictureBox1.Image = Nothing
End If
End Sub
回答1:
You can use AddHandler to wire up any number of PictureBoxes to the same handler methods. The "sender" parameter in those handlers will tell you which PictureBox is the source of the event. Note in the code below that none of the methods have the "Handles" clause at the ends of them; everything is being wired up dynamically in the Load() event of the Form. All PictureBoxes that are contained directly by the Form will be wired up. In my test project I placed ten of them on the Form:
Public Class Form1
Private Source As PictureBox = Nothing
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For Each PB As PictureBox In Me.Controls.OfType(Of PictureBox)()
PB.AllowDrop = True
AddHandler PB.MouseMove, AddressOf PBs_MouseMove
AddHandler PB.DragEnter, AddressOf PBs_DragEnter
AddHandler PB.DragDrop, AddressOf PBs_DragDrop
AddHandler PB.DragOver, AddressOf PBs_DragOver
Next
End Sub
Private Sub PBs_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
Dim PB As PictureBox = DirectCast(sender, PictureBox)
If Not IsNothing(PB.Image) AndAlso e.Button = Windows.Forms.MouseButtons.Left Then
Source = PB
PB.DoDragDrop(PB.Image, DragDropEffects.Copy Or DragDropEffects.Move)
End If
End Sub
Private Sub PBs_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
If My.Computer.Keyboard.CtrlKeyDown Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub PBs_DragOver(sender As Object, e As DragEventArgs)
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
If My.Computer.Keyboard.CtrlKeyDown Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub PBs_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
Dim PB As PictureBox = DirectCast(sender, PictureBox)
PB.Image = e.Data.GetData(DataFormats.Bitmap)
If e.Effect = DragDropEffects.Move Then
If Not (PB Is Source) Then
Source.Image = Nothing
End If
End If
End Sub
End Class
From the comments:
Using the AddHandlers, is it possible to "swap" two pictures with one drag/drop?
It certainly is; and is quite easy. With the change in code below to DragDrop(), the default behavior for Move will be to Swap the two images. If they hold down Ctrl to Copy then it will overwrite without swapping:
Private Sub PBs_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
Dim PB As PictureBox = DirectCast(sender, PictureBox)
Dim tmpImage As Image = PB.Image ' store the current image
PB.Image = e.Data.GetData(DataFormats.Bitmap) ' change it to the dropped image
If e.Effect = DragDropEffects.Move Then
If Not (PB Is Source) Then
Source.Image = tmpImage ' put the stored image in the source picturebox (swap)
End If
End If
End Sub
From the comments:
How would I exclude one specific picturebox from being "draggable"?
In the Load() event, you'd check for the PB to exclude in the loop. For example, this would exclude "PictureBox1":
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For Each PB As PictureBox In Me.Controls.OfType(Of PictureBox)()
If Not (PB Is PictureBox1) Then
PB.AllowDrop = True
AddHandler PB.MouseMove, AddressOf PBs_MouseMove
AddHandler PB.DragEnter, AddressOf PBs_DragEnter
AddHandler PB.DragDrop, AddressOf PBs_DragDrop
AddHandler PB.DragOver, AddressOf PBs_DragOver
End If
Next
End Sub
回答2:
Regarding my last question about excluding certain pictureboxes from being draggable (see last comment), I discovered the "enabled" property of pictureboxes. It was quite easy to turn that property on or off as needed.
来源:https://stackoverflow.com/questions/27537362/drag-and-drop-images-between-numerous-picture-boxes