Drag and Drop
Overview
As a Windows user, you have undoubtedly used drag and drop techniques to copy or move files from one folder to another, to delete a file by dragging it to the recycle bin, and to perform actions in various application programs. The drag-and-drop features in Visual Basic allow you to incorporate this functionality in the programs you develop.
The action of holding a mouse button down and moving a control is called dragging, and the action of releasing the button is called dropping.
Basically, a control can work as a source or as a destination of a drag-and-drop operation. Visual Basic supports two drag-and-drop modes, automatic or manual. In automatic mode, you just have to set a property at design time or at run time and let Visual Basic do everything. Conversely, in manual mode you have to respond to a number of events that occur while dragging is in progress, but in return you get better control over the process.
To incorporate drag and drop functionality in your VB programs, you use a handful of properties, events, and methods.
Properties
The two properties involved are DragMode, which specifies whether Automatic or Manual dragging will be used, and DragIcon, which specifies what icon is displayed when the control is dragged.
Events
The two events involved are DragDrop, which occurs when a control is dropped onto the object, and DragOver, which occurs when a control is dragged over the object.
Method
The Drag method starts or stops manual dragging.
All controls except menus, timers, lines, and shapes support the DragMode and DragIcon properties and the Drag method. Forms recognize the DragDrop and DragOver events, but they don't support the DragMode and DragIcon properties or the Drag method.
Sample Program
The sample program presented in this topic is a child's game where the objective is to match a picture to its corresponding word.
When the program runs, a splash screen is briefly displayed:
The main screen then displays, presenting the user with 10 pictures in a column on the left, and 10 words in a column on the right. Empty slots appear next to each word. The user's task is to drag the picture on the left to the slot next to the matching word on the right.
Screen shot of play in progress – the player has dragged some of the pictures on the left to the appropriate spot on the right:
When done, the player clicks "Check Score", and the program evaluates the results. A red "X" is placed next to wrong answers; a green check is placed next to correct answers. The yellow label near the top right of the screen appears indicating the percent correct and the number of tries so far.
The players clicks the "Reset" button, which causes the wrong items to be returned back to their respective slots on the left (and causes the Reset button to disappear).
The player corrects his mistakes and clicks "Check Score" once more. (This time all answers are correct. At this point, the user would either start a new game or exit.)
Designing the Sample Program
Icons Used
The pictures used for word-matching comes from an icon set called "Kid Icons", obtained from the site www.iconfactory.com. A subset of the icon set, shown below, is included in the download.
The program uses a handful of other icons from custom icon sets. These are included in the download as well.
Project Structure
As indicated by the screen-shot below, the project consists of two forms (a splash screen "frmSplash" and the main game form "frmWordGame") and one .BAS module ("modCommon").
Contents of modCommon.bas
The modCommon module contains the CenterForm Sub, which is a generic routine to center a form on the screen:
Option Explicit
'------------------------------------------------------------------------
Public Sub CenterForm(pobjForm As Form)
'------------------------------------------------------------------------
With pobjForm
.Top = (Screen.Height - .Height) / 2
.Left = (Screen.Width - .Width) / 2
End With
End Sub
Splash Screen (frmSplash) Design
The design-time version of the splash screen is shown below. To build the app, place the controls on the form as shown and set the properties as indicated by the callouts. Note: The backcolor of the form (&H00D8E9EC&) is what I refer to as "XP Beige", and can be a nice alternative to the default gray color for forms.
Splash Screen (frmSplash) Code
The Splash Screen form contains code for two events, as discussed below.
The Form_Load Event
The Form_Load event calls the CenterForm Sub, which centers the form on the screen.
Private Sub Form_Load()
CenterForm Me
End Sub
The tmrSplash_Timer Event
This event will fire after the two-second Interval (set at design-time) has elapsed. The main form ("frmWordGame") will be displayed, and the splash screen will unload itself.
Private Sub tmrSplash_Timer()
frmWordGame.Show
Unload Me
End Sub
Main Screen (frmWordGame) Design
The main screen of the application, as it looks at design-time, is shown below:
With the aid of screen-shots with callouts, the names and property settings for each control on the form, as well as for the form itself, will be shown in parts.
The properties for the form itself as well as the items at the top of the form are shown in the callouts below. Recall that setting the Font property for a form makes that font the default for any control placed on the form – so all controls that display text will have a font of Tahoma, Size 12 unless explicitly changed via that control's Font property.
The properties for the various control arrays used in the main part of the form are shown in the callouts below:
The properties for the command buttons and other controls used on the right-hand side of the main part of the form are shown in the callouts below:
The cluster of images at the bottom of the form comprise the "image repository" for this application. The Visible property for all of these images is set to False at design-time and are used as needed by the application during run-time. All of the images except for the three shown with the callouts below form the "image bank" of 25 images for the game, 10 of which are selected at random at the start of a new game.
The three "non image bank" images are:
The 25 "image bank" images are shown circled below.
This is an Image control array named imgSourcePic, indexed 0 to 24. The BorderStyle property for all is set to 1 – Fixed Single, and the Visible property for all is set to False. The Tag property for each image has been set to a word that describes the image. It is the content of the Tag property that will be used when presenting the word list to the user during game play.
The table below shows what picture has been assigned to which element of the control array along with the content of the Tag property.
Index |
Tag |
Picture |
0 |
airplane |
Airplane.ico |
1 |
balloons |
Balloons.ico |
2 |
baseball glove |
Baseball Glove.ico |
3 |
pencil |
Big Pencil.ico |
4 |
bone |
Dog Bone.ico |
5 |
dog |
Doggie.ico |
6 |
fire plug |
Fire Plug.ico |
7 |
fish |
Fishy.ico |
8 |
ice cream cone |
Ice Cream.ico |
9 |
ladybug |
Lady Bug.ico |
10 |
moon |
Moon.ico |
11 |
computer |
My Computer.ico |
12 |
house |
My House.ico |
13 |
paddle ball |
Paddle Ball.ico |
14 |
popsicle |
Popsicle.ico |
15 |
present |
Present.ico |
16 |
heart |
Red Heart.ico |
17 |
robot |
Robot.ico |
18 |
castle |
Sandcastle.ico |
19 |
submarine |
Submarine.ico |
20 |
sun |
Sun.ico |
21 |
traffic light |
Traffic Light.ico |
22 |
tree |
Tree.ico |
23 |
spaceship |
UFO.ico |
24 |
star |
Yellow Star.ico |
Main Screen (frmWordGame) Code
The General Declarations Section
One form-level variable is declared for number of tries.
Private mintNumTries As Integer
The Form_Load Event
The Form_Load event calls the CenterForm Sub, which centers the form on the screen. This is followed by a call to the NewGameInit Sub.
'------------------------------------------------------------------------
Private Sub Form_Load()
'------------------------------------------------------------------------
CenterForm Me
NewGameInit
End Sub
The NewGameInit Sub
The purpose of this Sub is to set up a new game. Ten images are selected at random from the "image bank" of 25 images and are loaded into the column of images on the left. The Tag properties of the ten selected images are then "mixed up" and used as the word list on the right. See the comments in the code for more detail.
'------------------------------------------------------------------------
Private Sub NewGameInit()
'------------------------------------------------------------------------
Dim blnNumberExists As Boolean
Dim intRandomNumber As Integer
Dim aintPicIndex(0 To 9) As Integer
Dim aintWordIndex(0 To 9) As Integer
Dim intX As Integer
Dim intY As Integer
'Reseed the VB's random number generator
Randomize
'Set the 10 pictures that will be used in this game ...
For intX = 0 To 9
' Get a random number between 0 and 24, make sure it is not
' one we already have ...
Do
intRandomNumber = GetRandomNumber(0, 24)
blnNumberExists = False
intY = 0
Do Until intY >= intX Or blnNumberExists
If intRandomNumber = aintPicIndex(intY) Then
blnNumberExists = True
Else
intY = intY + 1
End If
Loop
Loop While blnNumberExists
' Store the new random number in the aintPicIndex array, so we
' can check for the next go-round ...
aintPicIndex(intX) = intRandomNumber
' Load the picture from the source image array to the
' current set for this game ...
imgPicToMatch(intX).Picture = imgSourcePic(intRandomNumber).Picture
imgPicToMatch(intX).Tag = imgSourcePic(intRandomNumber).Tag
' set the image's drag icon to match its picture
imgPicToMatch(intX).DragIcon = imgPicToMatch(intX).Picture
' allow automatic dragging
imgMatchingPic(intX).DragMode = vbAutomatic
Next intX
' Using the Tag property of the 10 images to be used in this game, "mix 'em up" and
' set the "word" label that appears to the right of the second column of images.
For intX = 0 To 9
Do
intRandomNumber = GetRandomNumber(0, 9)
blnNumberExists = False
intY = 0
Do Until intY >= intX Or blnNumberExists
If intRandomNumber = aintWordIndex(intY) Then
blnNumberExists = True
Else
intY = intY + 1
End If
Loop
Loop While blnNumberExists
aintWordIndex(intX) = intRandomNumber
lblWord(intX).Caption = imgPicToMatch(intRandomNumber).Tag
Next intX
' No need to see the "scoring box" or Reset button at this time ...
lblScoreMessage.Visible = False
lblReset.Visible = False
cmdReset.Visible = False
End Sub
The GetRandomNumber Function
This function, used by the NewGameInit Sub, returns a random integer that falls within the range of the two arguments passed.
'------------------------------------------------------------------------
Private Function GetRandomNumber(pintLowerBound As Integer, _
pintUpperBound As Integer) _
As Integer
'------------------------------------------------------------------------
' This function will return a random integer that falls with the range
' of the two arguments passed.
GetRandomNumber = Int((pintUpperBound - pintLowerBound + 1) * Rnd + pintLowerBound)
End Function
The imgMatchingPic_DragDrop Event
This code is triggered when the user drops an image from the left-hand column to a slot in the right-hand column. See the comments in the code for more detail.
'------------------------------------------------------------------------
Private Sub imgMatchingPic_DragDrop(Index As Integer, _
Source As Control, _
X As Single, _
Y As Single)
'------------------------------------------------------------------------
' This event occurs when the user has dragged an image from the
' left-hand column of images (the "imgPicToMatch" control array) and
' has released the mouse to drop the image into an empty slot in the
' right-hand column (the "imgMatchingPic" control array).
' The image being dragged from the imgPicToMatch control array is
' passed as the "Source" argument to this event.
' (This event will also occur if the user has dropped an image into
' a slot on the right, but changes their mind and drags it from that
' slot on the right to a different slot on the right.)
' If the user attempts to drop the image into a slot that already
' has a picture in it, do nothing and exit the sub ...
If imgMatchingPic(Index).Picture <> LoadPicture() Then Exit Sub
' Populate the Picture and Tag properties of the "target" image
' with those of the "Source" image ...
imgMatchingPic(Index).Picture = Source.Picture
imgMatchingPic(Index).Tag = Source.Tag
' Set the DragIcon of the target image to be its Picture (in case
' the user wants to drag the picture back out because they changed
' their mind) ...
imgMatchingPic(Index).DragIcon = imgMatchingPic(Index).Picture
' Clear the Picture and DragIcon from the "Source" image ...
Source.Picture = LoadPicture()
Source.DragIcon = LoadPicture()
End Sub
The imgPicToMatch_DragOver Event
This event occurs when a user is dragging an image over a slot in the left-hand column. See the comments in the code for more detail.
'------------------------------------------------------------------------
Private Sub imgPicToMatch_DragOver(Index As Integer, _
Source As Control, _
X As Single, _
Y As Single, _
State As Integer)
'------------------------------------------------------------------------
' This event occurs when a user is dragging an image over an element
' in the left-hand column (the imgPicToMatch control array). If the
' image is "legal" for the slot being "hovered over", then the mouse
' icon will be the image that goes in that slot; otherwise, the mouse
' icon will be the "No" symbol.
' This can occur in the following situations:
' (1) The user is initally dragging an image from the left-hand side
' but hovers over another image or empty slot in the left-hand column
' prior to either dropping it either into an empty slot on the right
' or back in its original slot on the left.
' (2) After the user has dropped the image into a slot on the right, but
' then changes their mind and decides to drag it back to its original
' slot on the left.
If State = 1 Then
' leaving (moving out of the control)
Source.DragIcon = Source.Picture
Else
If imgPicToMatch(Index).Tag = Source.Tag Then
Source.DragIcon = Source.Picture
Else
Source.DragIcon = imgNo.Picture
End If
End If
End Sub
The imgPicToMatch_DragDrop Event
This event occurs when a user attempts to drop an image into a slot in the left-hand column. See the comments in the code for more detail.
'------------------------------------------------------------------------
Private Sub imgPicToMatch_DragDrop(Index As Integer, _
Source As Control, _
X As Single, _
Y As Single)
'------------------------------------------------------------------------
' This event occurs when the user attempts to drop an image into a
' slot of the left-hand column (the imgPicToMatch control array).
' This will typically occur after the user moved an image to a slot on
' the right, but then decides to move it back to its original slot on
' the left.
' However, this event will also occur if the user attempts to move an
' image from one slot on the left to another slot on the left (but the
' "If" tests in the code will not allow the drop to take place).
' If the user attempts to drop the image into a slot that already
' has a picture in it, do nothing and exit the sub ...
If imgPicToMatch(Index).Picture <> LoadPicture() Then Exit Sub
' Make sure that this is the original slot for the image being dropped ...
If imgPicToMatch(Index).Tag = Source.Tag Then
' Populate the Picture and DragIcon properties of the "target" image
' with those of the "Source" image ...
imgPicToMatch(Index).Picture = Source.Picture
imgPicToMatch(Index).DragIcon = Source.Picture
' Clear the Picture and DragIcon from the "Source" image ...
Source.Picture = LoadPicture()
Source.DragIcon = LoadPicture()
End If
End Sub
The cmdCheckScore_Click Event
This code executes when the user clicks the "Check Score" button, typically after moving all of the images from the left-hand column to the appropriate slots in the right-hand column. The Tags of the images are checked against the corresponding verbiage in the word label and are "marked" accordingly. If the user scored less than 100%, they are given the opportunity to "reset" the wrong answers and try again.
'------------------------------------------------------------------------
Private Sub cmdCheckScore_Click()
'------------------------------------------------------------------------
Dim intX As Integer
Dim intNumCorrect As Integer
Dim intPercentCorrect As Integer
Dim strCongrats As String
Dim strTries As String
' Loop thru the imgMatchingPic control array and "mark" the answer
' as correct or wrong ...
For intX = 0 To 9
If imgMatchingPic(intX) = LoadPicture() Then
' If the picture was left blank, then mark it wrong (place
' the "red X" next to the image) ...
imgMark(intX).Picture = imgWrong.Picture
ElseIf imgMatchingPic(intX).Tag = lblWord(intX).Caption Then
' If the Tag of the image that was dropped into the slot
' matches the caption of the corresponding element of the
' lblWord Label control array, then mark it correct (place
' the green checkmark next to the image) and increment the
' counter for the number of correct answers.
imgMark(intX).Picture = imgCorrect.Picture
intNumCorrect = intNumCorrect + 1
Else
' The Tag of the image that was dropped into the slot does
' NOT match the caption of the corresponding element of the
' lblWord Label control array, so mark it wrong (place
' the "red X" next to the image) ...
imgMark(intX).Picture = imgWrong.Picture
End If
' Disable automatic dragging for the moment by setting the DragMode
' to Manual ...
imgMatchingPic(intX).DragMode = vbManual
Next
' Increment the number of tries for this game ...
mintNumTries = mintNumTries + 1
' Calculate the percent correct ...
intPercentCorrect = (intNumCorrect / 10) * 100
' Formulate a message indicating how many answers are correct and in how
' many tries ...
If mintNumTries = 1 Then
strTries = "try"
If intPercentCorrect = 100 Then
strCongrats = "Congratulations! "
End If
Else
strTries = "tries"
End If
Beep
lblScoreMessage.Visible = True
lblScoreMessage.Caption = strCongrats & "You scored " & _
intPercentCorrect & "% in " & mintNumTries & _
" " & strTries & "."
' If the user did not get a perfect score, make the Reset button visible
' so they can go back and correct the answers they got wrong ...
If intPercentCorrect < 100 Then
lblReset.Visible = True
cmdReset.Visible = True
cmdReset.Default = True
Else
lblReset.Visible = False
cmdReset.Visible = False
End If
End Sub
The cmdReset_Click Event
This code executes when the user clicks the "Reset" button, which they can only do after they have checked their score and received less than 100%. This Sub moves all "wrong answer" images back to their original slots on the left.
'------------------------------------------------------------------------
Private Sub cmdReset_Click()
'------------------------------------------------------------------------
Dim intX As Integer
Dim intY As Integer
' Loop thru the imgMatchingPic control array ...
For intX = 0 To 9
' If the user got the answer wrong ...
If imgMatchingPic(intX).Tag <> lblWord(intX).Caption Then
' If they left the image blank, then just clear the "red X" mark ...
If imgMatchingPic(intX).Picture = LoadPicture() Then
imgMark(intX).Picture = LoadPicture()
Else
' Otherwise, move the picture back to its original slot on the
' left and clear it from the wrong slot on the right ...
For intY = 0 To 9
If imgPicToMatch(intY).Tag = imgMatchingPic(intX).Tag Then
imgPicToMatch(intY).Picture = imgMatchingPic(intX).Picture
imgPicToMatch(intY).DragIcon = imgPicToMatch(intY).Picture
imgMatchingPic(intX).Picture = LoadPicture()
imgMark(intX).Picture = LoadPicture()
Exit For
End If
Next
End If
' Set the DragMode for this slot back to Automatic ...
imgMatchingPic(intX).DragMode = vbAutomatic
End If
Next
' Remove the Reset button from the screen at this time ...
lblReset.Visible = False
cmdReset.Visible = False
End Sub
The cmdNewGame_Click Event
This code executes when the user clicks the "New Game" button. It clears the "marks" as well as the images in the right-hand column and calls the NewGameInit Sub to prepare a new game.
'------------------------------------------------------------------------
Private Sub cmdNewGame_Click()
'------------------------------------------------------------------------
Dim intX As Integer
' Clear the "marks" as well as the images in the right-hand column ...
For intX = 0 To 9
imgMark(intX).Picture = LoadPicture()
imgMatchingPic(intX).Picture = LoadPicture()
Next intX
' Reset number of tries counter ...
mintNumTries = 0
' Prepare for a new game ...
Call NewGameInit
' Make the "Check Score" button the default button ...
cmdCheckScore.Default = True
cmdCheckScore.SetFocus
End Sub
The cmdExit_Click Event
This code executes when the user clicks the "Exit" button. It issues the Unload statement, which triggers the Form_Unload event.
'------------------------------------------------------------------------
Private Sub cmdExit_Click()
'------------------------------------------------------------------------
Unload Me
End Sub
The Form_Unload Event
This code displays a prompt to confirm that the user really wants to quit, and if so, proceeds with the unload, which will cause the program to end.
'------------------------------------------------------------------------
Private Sub Form_Unload(Cancel As Integer)
'------------------------------------------------------------------------
Dim inUserResponse As Integer
inUserResponse = MsgBox("Are you sure you want to exit?", _
vbYesNo + vbQuestion + vbDefaultButton1, _
"Young Challenger Word Game")
If inUserResponse = vbNo Then
Cancel = 1
End If
End Sub
Download the VB project code for the example above here.