Coding a Euchre Game, Part 5: Modality is a crutch (Matt Gertz)

Anthony D. Green [MSFT]

Coding a Euchre Game, Part 5: Modality is a crutch

It’s really tempting to rely on modal dialogs in a program.  It forces the user to pay attention to what you feel is the most important thing.  However, modal dialogs can also be frustrating to users and you should be careful about when you use them.  To illustrate this point, let’s consider the Euchre game again.  At some point, the user is going to be asked to play a card.  Ideally, you don’t want the user to do anything else because there isn’t anything else the user can do – right?  Well, no exactly.  Remember that the user needs to be able to start a new game, or possibly close the game.  I talked a bit about this earlier in parts three and four of this series, but only as far as adding a message pump while the AI was operating.  In this case, though, it’s not the AI that’s active, but the player.  If the user is presented with a modal dialog, he or she is effective barred from closing the app (or restarting it), unless you do something lame like add a “quit game” or “new game” button to the dialog which the user would have to look at on every hand.  Furthermore, the modal dialog just won’t look as good for gameplay – the user will be expecting to click on a card instead, as that’s far more intuitive.

We need a better system; some way to get the user to click the card and yet give them a chance to quit or restart the game as well.  Furthermore, we want them to click a valid card – Euchre is a game where you have to play the same suit that was led (if you have such a card). This all actually turns out to be easy; we’ll just enable the relevant cards and add a message pump to handle events:

            If Seat = Seats.Player Then ‘ This is the human being player

 

               ‘ First, turn on the “play a card” sign:

                Table.SelectLabel.Text = My.Resources.Notice_PlayACard

 

              ‘ Assume the player has no cards which follow suit

                Dim AnyValid As Boolean = False

 

              ‘ Check each remaining card in the player’s hand

              ‘ to see if it would follow suit

                Dim i As Integer

                For i = 0 To 4

                    If Me.CardsHeldThisHand(i) IsNot Nothing Then

                        ‘ Note that the leader can play any card;

                        he/she selects the suit to be followed.

If Table.LeaderThisTrick <> Seat Then

                            If Not CardBelongsToLedSuit(Table, _

 Me.CardsHeldThisHand(i)) Then

‘ Wrong suit – disable the card

                                Table.TableTopCards(Seats.Player, _

i).Enabled = False

                            Else

                                AnyValid = True

                            End If

                        End If

                    Else

                        ‘ Disable any labels representing

‘ already-played cards.

                        Table.TableTopCards(Seats.Player, i).Enabled = False

                    End If

                Next

 

                If AnyValid = False Then ‘ Nothing of suit –

‘ can play whatever you want,

‘ so re-enable unplayed cards

                    For i = 0 To 4

                        If Not Me.CardsHeldThisHand(i) Is Nothing Then

                            Table.TableTopCards(Seats.Player, _

i).Enabled = True

                        End If

                    Next

                End If

 

                Table.SelectLabel.Visible = True

                Table.SelectLabel.Update()

                Table.PlayerIsPlayingACard = True

                Table.SetPlayerCursorToHand(True)

 

                ‘ Do a message pump here:

                Do While Table.PlayerIsPlayingACard = True

                    My.Application.DoEvents()

                    If Table.Exiting = True Then

                        Dim e As New EuchreException(“ExitGame”)

                        Throw e

                    End If

                    If Table.Restarting = True Then

                        Dim e As New EuchreException(“NewGame”)

                        Throw e

                    End If

                Loop

                Table.SelectLabel.Visible = False

                For i = 0 To 4

                    Table.TableTopCards(Seats.Player, i).Enabled = True

                Next i

                Table.Update()

                index = Table.SelectedCard ‘ Get the card the player picked

            Else

                ‘ Not a human being – let the AI pick a card

                index = AutoPlayACard(Table)

            End If

        End If

 

And then we’d go on to use the value of “index” to graphically play the selected card, mark it as played, etc.  Now, the above relies on the member variable PlayerIsPlayingACard in order to exit the message pump.  So, as you’ve probably guessed, there’s an event handler out there which will change this value whenever the “Click” event happens on a card:   

    Private Sub PlayerCard_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

        Handles PlayerCard1.Click, PlayerCard2.Click,

PlayerCard3.Click, PlayerCard4.Click, PlayerCard5.Click

        If PlayerIsDroppingACard = True OrElse _

PlayerIsPlayingACard = True Then

            If sender Is PlayerCard1 Then

                SelectedCard = 0

            ElseIf sender Is PlayerCard2 Then

                SelectedCard = 1

            ElseIf sender Is PlayerCard3 Then

                SelectedCard = 2

            ElseIf sender Is PlayerCard4 Then

                SelectedCard = 3

            ElseIf sender Is PlayerCard5 Then

                SelectedCard = 4

            End If

 

            PlayerIsDroppingACard = False

            PlayerIsPlayingACard = False

            SetPlayerCursorToHand(False)

        End If

    End Sub

 

To create this handler, I just double-clicked on the first player card label on the form to create the handler for that card, typed in the other four labels’ click events to the handles clause in the method signature, and filled in the code.  (I also renamed the handler from PlayerCard1_Click to just PlayerCard_Click to indicate to me that it handles all of the player card clicks, but that’s just a cosmetic change and is not essential.)

However, in Euchre, sometimes the user feedback needs to be more complicated than simply clicking a card.  The user needs to be able to “bid” on trump at the beginning of the round.  I’ll need to explain a bit of the rules here to make that clearer: in Euchre, you use a deck of 24 cards (9 through A) and have four players.  Each player gets 5 cards, and this leaves 4 remaining.  The 4 are referred to as the “Kitty,” and the top one is turned up.  Each player gets a turn to decide if that want that card’s suit to be trump (i.e., will his or her team capture at least 3 of the 5 hands if that suit is trump).  If the player wants that card’s suit to be trump, the dealer picks it up and discards one of their own cards, meaning that the dealer will have at least one trump card.  If nobody wants that card’s suit to be trump, then there is another bidding round where players either pass or select any of the remaining trump suits (in order) until a suit is selected.  If everyone passes, then either the deal passes to the next player, or the current dealer is “stuck” selecting trump, depending on the rules you prefer.

So, we need a way to let the user bid on the trump.  We might be tempted to use a modal dialog here, but if we do, we’re blocking the close and new game events (as well as any ability to reposition the main window while the modal dialog is up).  Modeless dialogs won’t be much help, either, since we’d have to set up some sort of messaging between them and the main form.  So, what do we do?  Well, let’s take a lesson from the “play a card” scenario.  Ideally, we need a bid control which will be enabled when the bidding occurs; otherwise, it should be invisible.  We’ll use the most powerful control we’ve got – the user control.

The user control is so powerful because it’s a control that you define yourself.  It’s like having a mini-form living inside another form.  To create a user control, simply choose “Add User Control” from the “Project” menu.  You’ll get the typical form editor coming up, to which you just add in whatever controls you need.  My two bidding user controls are attached to this post, so go ahead & take a look.  Either of them is one control as far as the main form is concerned, although internally they’re made up of multiple controls.  The user controls show up in the form’s toolbox after building the project using Project/Build, so I’ll do that, then I’ll drag both controls to the center of the form.  To make sure that they don’t trigger any events before they should, I’ll mark them as Enabled = False, and I’ll hide them with “Visible=False.”   It’s then a simple thing to enable them in the main application during the bid, as I do here for the smaller one on the first bidding round:

            Table.BidControl.Visible = True

            Table.BidControl.Enabled = True

            Table.BidControl.BringToFront()

            Table.BidControl.Update()

            ‘ Do a message pump here:

            Do While Table.PlayerIsBidding = True

                My.Application.DoEvents()

                If Table.Exiting = True Then

                    Dim e As New EuchreException(“ExitGame”)

                    Throw e

                End If

                If Table.Restarting = True Then

                    Dim e As New EuchreException(“NewGame”)

                    Throw e

                End If

            Loop

 

And when “PlayerIsBidding” changes to False (as we’ll code it to do when the OK button is pressed on the bid control), we’ll simply get the state of the user control and then disable & hide it.  The player’s cards are disabled at this point in the game, so we don’t have to worry about handling any errant clicks on them. Thus, no modality is required; we got the user to focus on the job at hand while not inconveniencing them with a locked main window. 

That’s not to say that modal windows are “bad” – it’s just a matter of knowing when to use them for maximum effectiveness.  In the completed VBEuchre game which I’ll be attaching in the final post, you’ll note that I use them for issues that would involve data loss, such as ending the game or starting a new one (“Are you sure you want to exit?”), or for cases where the dialog is likely to be up only rarely and briefly (such as the “About…” dialog and the “New Game” dialog).

In the next part of this series, I’ll be talking about including sound in your application, including text-to-speech.  See you then!

EuchreBidDialogs.jpg

0 comments

Leave a comment

Feedback usabilla icon