Canfield (C# Project) - WPF/2010 version

Overview

I decided to revisit my Canfield program with an eye towards graphics and the UI. In addition, I wanted to separate the PokerDeck class out as an independent DLL. Also, since VS2010 recently came out, and with the increasing emphasis on WPF, I wanted some practice in those arenas as well. So I took the basic logic from my previous project and made the following changes:

There are still enhancements that could be made. Rather than dragging cards from pile to pile, the user clicks on a card to pick it up and then clicks elsewhere to put it back down. This leads to more clicking that people are most likely used to. Also, there's no save function, and undo only works on the last action. Even so, I think this is a significant improvement on the previous version.

Initial state
Fig. 1: Initial state
Mid game
Fig. 2: Mid game view

Running the program

This is an online-only implementation, with the following prerequisites.

If these components are already installed, you can run the application now (apparently requires Internet Explorer). Otherwise, set it up and run it (does not require Internet Explorer).

Major concepts explored and learned

Separate DLL

Using classes as stand-alone DLLs is reasonably straightforward. I created a new Class Library project, deleted the default file created, and imported the CS files I'd created for the earlier version of Canfield. This could then be added as a reference to a new program. From the perspective of the base code logic, it doesn't matter much whether a class is included as an embedded file or as a reference. In short, this wound up being as easy and straightforward as I'd hoped.

I made two major changes. First, I changed the PokerDeck class to CardDeck. This was because I'd named the DLL file PokerDeck as well, and I was getting an error due to the name conflict. I decided it was easier to change the name of the individual class. Second, I added code to allow for images to be associated with cards.

Embedded graphics resources

This was a bit trickier, in part because Googling on the topic turned up an array of different strategies. It was also two waves of challenge, because once I got the images embedded (as bitmaps), I discovered that XAML images aren't of the same format. This lead to another wander through the Internet for a set of conflicting tutorials. Obviously, this implies that my solution may not be the "ideal" solution, but it's working and I'm not going to tweak it for fear of breaking it again. That said, here's what I wound up doing.

To embed the image files, I first went to Project>Properties...>Resources with the PokerDeck DLL open and imported the files. I'd found a set of PNG files online (apparently for free use, but if I get a C&D request from the creator, I can swap them out), including 54 faces and two different colors of backs. I renamed them as I saw fit for my purposes. After importing them, I went to the Solution Explorer and opened Resources, selected all the files, and changed the Build Action property to "Embedded Resource." Now the graphics files are part of the deployed DLL file.

The image files were now of type Bitmap, but I was designing my WPF UI with image tags, which was not nearly as straightforward a conversion as I was hoping. After wading through a variety of solutions that didn't work, I got one to work. It turned out not to be as intricate as I'd feared after the hours of searching. I added the code to the PlayingCard class as a property. Here are both public properties I added:

public Bitmap Face
{
  get
  {
    return imgFace;
  }

  set
  {
    imgFace = value;
  }
}

public BitmapSource ImageSource
{
  get
  {
    return Imaging.CreateBitmapSourceFromHBitmap(
    imgFace.GetHbitmap(), IntPtr.Zero, System.Windows.Int32Rect.Empty,
    BitmapSizeOptions.FromWidthAndHeight(72, 96));
  }
}

Note that the width and height are hard-coded; these values were based on the graphics files I used.

Because I had two card backs available, I decided to use both and add code to change between them. Similar code would be used if other deck sets were added. This was added to the CardDeck class:

private string strBack = "blue_hor";
public CardDeck(string BackColor)
{
  strBack = BackColor;
  NewDeck();
}

public PlayingCard CardBack
{
  get
  {
    return cardBack;
  }
}

public void NewDeck(string BackColor)
{
  strBack = BackColor;
  NewDeck();
}

public void NewDeck()
{
...
  ChangeBack();
}

private void ChangeBack()
{
  cardBack.Face = (Bitmap)PokerDeck.Properties.Resources.ResourceManager
  .GetObject(strBack);
}

public string Back
{
  set
  {
    strBack = value;
    ChangeBack();
  }
}

Within implementing programs, the card back can be specified when the object is declared (CardDeck cd = new CardDeck(strBack);), when the object is initialized (cd.NewDeck(strBack);), or on the fly afterward (cd.Back = strBack;).

WPF/XAML

The rules window is about the simplest an XAML window can be, not including the styles:

Rules window
Fig. 3: Rules window (original version)

This is the underlying code:

<Window x:Class="Canfield.RulesWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Canfield Rules" Height="500" Width="500" 
  WindowStartupLocation="CenterOwner" Icon="/Canfield;component/Images/penum.ico">
  <FlowDocumentScrollViewer>
    <FlowDocument>
	  ... FlowDocument style definitions here ...
      <Paragraph Style="{StaticResource Header}">
        Canfield - Overview
      </Paragraph>
      ... Other paragraphs here ...
    </FlowDocument>
  </FlowDocumentScrollViewer>
</Window>

The options window is also fairly simple. I like the easy, clear way of grouping radio button controls in XAML:

Options window
Fig. 4: Options window

This is the underlying code for a group of radio buttons (formatting details omitted):

<Label Content="Card back" Name="lblCardBack"/>
<RadioButton Content="Blue" Name="radBlue" GroupName="grpBack"/>
<RadioButton Content="Red" Name="radRed" GroupName="grpBack"/>

... and for the clickable buttons:

<Button Content="Apply" Name="cmdApply" Click="Apply_Click"/>
<Button Content="Cancel" Name="cmdCancel" IsCancel="True"/>

The Cancel button doesn't need a click event because IsCancel="True" automatically closes the modal window with a return value of false.

The main Canfield window is the most complex. The basic card display is accomplished through several groups of Image elements, many of which are set to null at any given time. For instance, the tableaux consist of four sets of thirteen Image elements; when the cards are dealt, only the top of these is filled with a visible card image. All told, the Canfield window contains 72 Image elements. In addition, under the "bottom" card image of each pile, there's a Label element containing a Textblock to provide a message to the user, such as "Play the Queen of Clubs" in figure 2. Here is the label/textblock definition for the Draw pile (upper left):

<Label Name="lblDraw" Opacity="0.5" Height="96" Width="72"
  HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
  HorizontalAlignment="Left" VerticalAlignment="Top" Margin="6,6,0,0"
  MouseLeftButtonUp="CardLocation_Click">
  <TextBlock TextWrapping="Wrap" TextAlignment="Center">
    Return cards to draw pile
  </TextBlock>
</Label>

The XAML for creating the menus and status bar was straightforward, with the note that multiple items docked to the right (or bottom) or listed from right-to-left (or bottom-to-top), not left-to-right. Where possible, I used standard commands.

<Menu DockPanel.Dock="Top">
  <MenuItem Header="Game">
    <MenuItem Command="New"/>
    <MenuItem Header="Options" Click="Options_Click"/>
...
  </MenuItem>
</Menu>
<StatusBar DockPanel.Dock="Bottom">
  <StatusBarItem DockPanel.Dock="Right">
    <TextBlock Name="sbCardsPlayed">Played: 0</TextBlock>
  </StatusBarItem>
...
  <StatusBarItem>
    <TextBlock Name="sbError">Ready
  </StatusBarItem>
</StatusBar>

The underlying logic for the Canfield program is basically unchanged since the previous version, with a few tweaks to clean up the code based on things I've learned in the last few months.

Shader Effect

One of the more frustrating details was getting the selected card to be shaded differently. Valerie had indicated a good deal of confusion about whether or not there was a selected card, so apparently having the card appear in the lower left wasn't enough of a visual clue. At first, I changed the opacity setting, but I didn't like the how the card below bled through. Adding a blur effect instead gave me a headache. So I went looking for how to add a shader effect, and was admittedly quite daunted with the complexity of the process. Eventually, I found Shazzam Tool, which greatly simplified the process. I decided to go with the canned ColorTone effect with some customized colors, and it only took about half an hour to tweak it out to my satisfaction.

Conclusions

These enhancements were more challenging for me than the original program, although this was largely due to dealing with vagaries of "How do I...?" with conflicting explanations on the Internet. Once I "got" something, it seemed fairly easy and obvious, and I wonder if that's not an obstacle for some of the documentation: Because it's so "obvious" once you know how to do something, people forget how to present it to someone who's never done it before.

However, despite the extra work, I think this is a significant UI improvement over the original version, and I'm glad I hammered out the details.

As for the program itself, I believe I've caught the bugs, although it has crashed for me twice during normal processing doing things it had been doing fine otherwise... I wonder if there's not an issue with clicking too quickly or otherwise not getting resources back in time. Please do let me know if you find any problems, have any questions, or if you just happen to find this page, enjoy the program or write-up, and want to say hello.