March 23rd, 2007

Coding a Euchre Game, Part 8: Remember Me, Show Me, Help Me (Matt Gertz)

Coding a Euchre Game, Part 8:  Remember Me, Show Me, Help Me

We’re getting close to wrapping up this series.  In this post, I want to cover several “clean up” topics, and then in the following post, I’ll talk about deployment and will attach the entire codebase for your perusal.

Settings

You’ll note, from earlier posts, that there are a lot of options around variations in the Euchre rules, options for AI personalities, options for sound, and even options for the names of the players.  Obviously, it would be really annoying if you had to specify those at the beginning of each session!  The best thing to do would be to persist the user’s choices for each option and reload them whenever the game is launched.  Now, in “Ye Olde Days,” what you’d typically do is create or open a user options file, figure out a format in which to store this data, and then write it or read it as appropriate.  This was a mildly annoying process in that you’d have to re-code your format (and potentially add code to be backwards-compatible) every time you added options, plus you’d have to figure out where to store the options, etc.  Fortunately, there’s a better way to do this in VS2005 – it’s so easy that once you start using it, you’ll never go back to doing it the old way!

The mechanism to use to implement setting persistence is My.Settings.  Using this mechanism allows you to treat each setting as if it were a property, *plus* with one line of code you can save or retrieve settings from an application data file that is automatically created for you – and you don’t even need to keep track of where the file is!  There’s also support for versioning, so that you can have different settings for different versions of your app.  Here’s how you do it:

(1)    Bring up the project’s properties (right-click the project, choose “Properties”), and click on the Settings tab.

(2)    You’ll notice a grid appears.  In the grid, choose a name for the setting (e.g., “PlayerName”), the type of the setting  (e.g., String), and the default value (e.g., “Player 1”).  Leave the scope as user.  (If you set it to “application,” then everyone would share the same options, which would be annoying in this case.)  Continue to do this for each setting, using Booleans for checkboxes, Integers for the voice indices, etc.  I’ve attached a picture of what mine look like.

(3)    Now, open up the options dialog, and double-click it to automatically create the  event handler for “Load.”  In that event handler, add the code to initialize the dialog with the user settings – for example:

        Me.PlayerName.Text = My.Settings.PlayerName

        Me.LeftVoiceCombo.SelectedIndex = My.Settings.LeftOpponentVoice

        Me.SoundOn.Checked = My.Settings.SoundOn

        Me.LeftOpponentCrazy.Checked = (My.Settings.LeftOpponentPlay = 1)

        ‘ Etc… for brevity’s sake, I’ve left out a lot of these

(4)    Then the other half of this is persisting out the settings, so double-click on the “OK” button to create its click event handler, and add code opposite of what you did before, such as the following:

        My.Settings.PlayerName = Me.PlayerName.Text

        My.Settings.LeftOpponentVoice = Me.LeftVoiceCombo.SelectedIndex

        My.Settings.SoundOn = Me.SoundOn.Checked

        If Me.LeftOpponentCrazy.Checked = True Then

            My.Settings.LeftOpponentPlay = 1

        ElseIf LeftOpponentNormal.Checked = True Then

            My.Settings.LeftOpponentPlay = 2

        Else

            My.Settings.LeftOpponentPlay = 3

        End If

        ‘ Etc… again, for brevity’s sake, I’ve left out a lot of these

 

That’s *it*; that’s all you have to do.  The settings will be persisted when the app closes, and loaded when the app is launched. You don’t have to do anything.  You can even hook up a “Reset” button which will call My.Settings.Reset to return the setting to the “factory defaults.”  The code to handle this is autogenerated for you in the normally-hidden files settings.settings.vb, application.myapp, etc., and the settings themselves are persisted in a (similarly normally-hidden) application data file called user.config in the user’s personal directory.  (See http://msdn2.microsoft.com/en-us/library/a65txexh(VS.80).aspx if you really want to know all of the gory details.) You can also turn off saving the settings at shutdown from the “Application” tab in the project’s properties (in which case you’d use My.Settings.Save to save them programmatically.)

One more note before we move on to the next topic – you should always think twice before storing sensitive information like passwords, etc., using user settings.  Although nominally confined to the user’s own directory, the data in the user file still stored in clear text, so why take the chance?

Rich Text Box Controls & Help

Edit boxes have been a mainstay of applications since graphical interfaces first began, but let’s face it; they’re terrible to deal with regarding formatting.  Rich Text Box controls are definitely the way to go if you need to do lots of fancy things with text.  I use a couple of them in my Euchre application, so let’s take a peek at those.

The first one I use is on the table form itself.  I use the rich text edit control to keep repeat back to the user what’s happening in the game.  For example, when Player 2 plays a card, I repeat back “Player 2 plays the Left Bower” or whatever the card was.  The idea here is that the user can scroll back through history and see what’s already happened.  Now, in the actual game of Euchre, players are forbidden from looking through tricks that have already been played, and this might seem to fly in the face of that rule, but this sort of counterbalances the impersonality of the game – in real life, the player would probably yell “Aha! My Left Bower takes all of your pitiful little cards… come to daddy…mine, mine, all mine…,” which would certainly stick in one’s memory in a way which the AI could never do (at least as coded).

So, I need a control that can take a lot of text and which can have a scroll bar.  That pretty well eliminates the normal edit box as well as the label control, so I’ve used a rich text box control here.  The initialization is fairly simple:  set ReadOnly to True, ScrollBars to Vertical, and the backcolor & font to whatever appeals to you (I’ve gone for a light yellow background with Copperplate Gothic  12pt. as the font).  I don’t need any validation on the control, so I set CausesValidation to False, and I’ve also set DetectUrls to False since I won’t have any and so I don’t need the control wasting time trying to figure that out.

Now, as far as using it goes – also very simple.  The following code does the trick for me:

    Public Sub UpdateStatus(ByVal s As String, _

Optional ByVal WhiteSpace As Integer = 1)

        Me.StatusArea.AppendText(s)

        If WhiteSpace > 0 Then

            For i As Integer = 1 To WhiteSpace

                Me.StatusArea.AppendText(vbCrLf)

            Next

        End If

        Me.StatusArea.Update()

    End Sub

 

Where the usage is something like (in the EuchrePlayer class):

  Dim s As New StringBuilder()

  s.AppendFormat(My.Resources.Notice_IPickItUpAlone, GetDisplayName(Table), _

     My.Resources.ResourceManager.GetString( _

          EuchreCard.GetSuitDisplayStringResourceName(Table.TrumpSuit)))

  Table.UpdateStatus(s.ToString)

 

To (in this example) format a string which says “Player 1 says ‘I’m picking it up and going alone – trump is spades’” and then applying it to the control without adding extra line spacing after it.

So far, so good – none of that seems earth-shattering.  However, I’ve got another rich text box that I use for Help.  Rather than jump in with the control, let’s start at the beginning and talk about Help.

Help

There’s nothing magic about supporting Help.  Help is just another event, albeit one that everyone is familiar with.  You may have noticed a few posts back that I added a “Help” menu item to my menustrip control.  I have two entries in that menu: “About…” and “Rules…”  Let’s take these separately:

“About…”

 Right-click the project, choose “Add” and then “Windows Form.”  Select “About Box” from the resulting dialog and give the file a name such as “EuchreAboutBox.”  Click “Add” to add it to the project.  (The “About” box will auto populate with the typical application information at runtime, although you can tweak it however you like by opening the form in the usual way.)  Now, double-click on the “About…” menu item on the menustrip and add these two lines to the resulting event handler:

                  Dim dlg As New EuchreAboutBox

          dlg.ShowDialog()

 

That’s it; all done.

“Rules…”

This is very similar to “About…”, but instead I’m going to add a normal “Windows Form” to the project instead of an About Box.  (You may prefer to have rules come up in the typical “search/index/contents” sort of help, but that’s a topic for a different time… I mean, it *is* just a card game, and anyway I want to demonstrate another property of the Rich Text Box.)  To this form I’m just going to add a rich text box and a button.  The button is labeled “Close,” and all it does is dismiss the form, so I set its its click event to Me.Close(), and otherwise I pretty much leave it alone.  The rich text box is similarly simple: I choose a font and background color that I like and set those in the properties, I set it to read-only, and give it a name such as “RtfRules.”  (“Scroll Bars” already defaults to “Both” so I leave it alone.)  Then, I add one line of code in the Form Load Event to leverage that property I alluded to before:

        Me.RtfRules.Rtf = My.Resources.VBEuchreRules

 

Which connects the Rich Text Box to an RTF file I have stored in my resource manager.  Yes, you can add RTF files to the resource manager, the same way you add any other resource!  In my case, I created a nice-looking “rules” file in Word, saved it as an RTF file, chucked it into the resource manager, and reference it as above.  If I need to change the RTF file, I just right-click the RTF file (which will show up in my project treeview) and choose “Open with Word,” then rebuild once I’ve saved the file again.  Using the Rich Text Box and the RTF file is simple, it looks nice, and Rich Text Boxes already have a property which can accept RTF as a target so I’m all set.  I just need to bring up the Rules form when requested:

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

 ByVal e As System.EventArgs) Handles RulesToolStripMenuItem.Click

        Dim rulesform As Object = My.Application.OpenForms(“EuchreRules”)

        If rulesform Is Nothing Then

            Dim x As New EuchreRules

            x.Show()

        Else

            CType(rulesform, EuchreRules).Activate()

        End If

    End Sub

 

This is a *modeless* dialog (so you can see the rules while playing), so I first check to see if the dialog already exists by leveraging the My.Application.OpenForms functionality.  If it doesn’t exist yet, I create it using Show() (not ShowDialog(), which would make it modal).  If it does exist, then I bring it to the front via Activate(), first casting the object to the form’s type so that I can access that method.

Of course, you can link it up to the F1 key as well.  Back in the form editor for the main form, select the “Rules…” item and set the ShortcutKeyDisplay property to “F1” (without quotes; that’s the text that will display on the menu next to the item)  and then also set the ShortcutKeys property to F1, to indicate the keystroke that will trigger the menu item.  That’s all it takes! 

(As an aside:  you could also do this without setting the Shortcuts property by handling the KeyUp event yourself, though I don’t recommend it.  To do this, right-click on the EuchreTable file and choose “View Code.”  Now, at the top of editor, you’ll see two dropdowns.  Change the left dropdown to say “(EuchreTable Events).”  In the right dropdown, select “KeyUp.”  A handler will be inserted for the KeyUp event, into which you could add this code:

    Private Sub EuchreTable_KeyUp(ByVal sender As Object, _

ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp

        If e.KeyCode = Keys.F2 Then

            NewGame()

        End If

    End Sub

 

That’s not as good as using the ShortcutKeys property, though, because it will also accept Alt+F1, Ctrl+F1, etc, and you’d have to add more logic to check for these keys.  However, it is useful to know how to add arbitrary event handlers to a form, which is why I mention it at all.)

Are we there yet?

Almost!  There are a couple of little clean-up things we need to do first that I thought I’d mention:

Tab stops

Many people use keystrokes to navigate around a form (instead of the mouse). To support this, we need to make sure that the tab order makes sense.  For the Euchre game, we’re mostly concerned about the options dialog and the bidding dialogs, to make sure that tabbing moves the selection appropriately.  (Ideally, I should do this for the cards themselves, to make them selectable by the keyboard, but I’ll leave that as an exercise for the reader. J) 

To fix the tab order for any form, just choose “Tab Order” from the View menu.  The current order will be shown; just click controls in the order in which you wish tabbing to occur, and then choose “Tab Order” again when you’re done.  You should note that you can nest tabbing orders next within groups of objects, so that (for example) you don’t tab through all of the radio buttons in a group – the user then tab to the active one and then use arrow keys to select.  The form editor will do this setup for you automatically when you’re selecting the tab order – it understands that group boxes contain things and that radio buttons are special.  I’ve attached a picture of the tab order for the Euchre options dialog to this post so you can see what I mean.

Application icons

Of course, you will want an icon for an application.  Visual Studio has a built-in icon editor, and you can get to it by bringing up the resource manager and either adding a new icon (via Add ResourceàAdd New Icon) or simply adding an existing *.ico file via Add ResourceàAdd Existing File, which will copy it into your project folder.  (Actually, if you already have an existing icon file to be used with a form or application, you need not add it to the RM for reasons that will become apparent below.)  I won’t attempt to describe the use of the icon editor to you in this post, as it’s not radically different from any other drawing program and many of you are already familiar with it anyway.  What’s more important is *how* you use the resulting icon.  What many people don’t realize is that each form has its own icon property, plus the application has an icon property as well, and these all need to be set to your icon(s) or else you’ll get the default Windows icon for them.

          To set the application’s icon:  Bring up the project properties, click the Application tab, select “Browse…” on the Icon field and point to your icon’s file (which will have been copied into the project’s folder).  This icon is what people will see attached to the EXE.

          To set a form’s icon, open the form and click the “…” button in the Icon property in the grid.  Again, browse to the appropriate file.  This is the icon that people will see in the upper-left corner of the form.

Note that both of these require you to browse to a file rather than use the resource, even though the resource may already be in the resource manager — this is an exception to the rule.  (The icons you point to get copied to separate resource managers which belong directly to the owning object — so don’t worry, you won’t be required to deploy a stand-along icon file!)

I use one 32×32 color icon in my application; you should probably have a 16×16 color icon as well, since that’s what the form is going to show.  Windows does a pretty good job squishing a 32×32 icon into a 16×16 space, but you might not want to leave it to chance.

So, I’ve covered all of the points I wanted to touch on regarding the actual coding of the game.  Next time, I’ll discuss deployment and will then post the final code.  Talk to you then!

–Matt–*

EuchrePost8picture.zip

0 comments