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.