XAML Playing Cards

Looking at the card games that ship with Windows XP, I think one of the most obvious opportunities for improvement is the look & feel of the playing cards.  These games use a shared library known as cards.dll to draw cards that look like… um… this:

Don’t those look so 20th century?  To prepare for migrating Internet Hearts, I’ve created an Avalon Playing Card control with built-in animations:

It can be set to any of the 52 cards (although no jokers currently).  I’ve also created a “Hand” control that mimics the hand dealt in the Win32 picture above:

And, of course, since they’re vector graphics, they look just as good no matter how much you zoom in:

You can get the source code here, which is compatible with the March CTP of Avalon.  I’ll post new versions as new builds of Avalon get released.

The .zip file contains a Visual Studio solution with two projects:

  • Cards, which builds Cards.dll containing the Card and Hand controls pictured above
  • Demo, a simple application demonstrating the use of the controls

In the Cards project, Cards.xaml defines the playing card control, which is just a button styled to look like a card:

 <Button Name=”CardButton” Style=”{StaticResource Card}” />

The style consists of a Viewbox (to enable scaling while maintaining the proper aspect ratio) and two rectangles: one painted with the desired card face and one painted with a shadow.  This painting is done with DrawingBrushes defined in the same file.  There are 53 of them: one per card face and one for the shadow.  For example, here’s my DrawingBrush for the 2 of Hearts:

 <DrawingBrush x:Key=”H2″ Stretch=”Uniform” Viewbox=”0 0 1468 2053″>
     <GeometryDrawing Brush=”#FFFFFF” Geometry=”M1467.5,1852.5c0,110-90,200-200,200h-1067c-110,0-200-90-200-200v-1652 c0-110,90-200,200-200h1067c110,0,200,90,200,200V1852.5z” />
     <GeometryDrawing Geometry=”M1467.5,1852.5c0,110-90,200-200,200h-1067c-110,0-200-90-200-200v-1652 c0-110,90-200,200-200h1067c110,0,200,90,200,200V1852.5z”>
      <GeometryDrawing.Pen><Pen Brush=”#000000″ /></GeometryDrawing.Pen>
     <GeometryDrawing Geometry=”M1080.5,1402.5c0,110-90,200-200,200h-293 c-110,0-200-90-200-200v-797c0-110,90-200,200-200h293c110,0,200,90,200,200V1402.5z”>
      <GeometryDrawing.Pen><Pen Brush=”#636BC1″ Thickness=”27″ /></GeometryDrawing.Pen>
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M63.709,718.157c-5.067-10.677-13.2-26.4-15.6-37.2c-7.2-27.6-14.4-60-6-88.8 c13.2-45.6,69.6-62.4,111.6-44.4c18,7.2,27.6,26.4,32.4,45.6c1.2,2.4,4.8,2.4,6,0c3.6-25.2,21.6-46.8,45.6-51.6 c48-8.4,88.8,26.4,94.8,72c4.8,38.4-8.4,74.4-27.6,108c-37.2,67.2-75.6,129.6-116.4,193.2 C140.51,854.957,97.31,788.957,63.709,718.157z” />
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M1406.117,1334.843c5.066,10.677,13.199,26.4,15.6,37.199 c7.199,27.601,14.4,60,6,88.801c-13.2,45.6-69.6,62.399-111.6,44.4c-18-7.201-27.601-26.4-32.4-45.601 c-1.2-2.399-4.801-2.399-6,0c-3.6,25.2-21.6,46.8-45.6,51.601c-48,8.399-88.801-26.4-94.801-72c-4.8-38.4,8.4-74.4,27.6-108 c37.201-67.201,75.601-129.601,116.4-193.2C1329.316,1198.042,1372.517,1264.042,1406.117,1334.843z” />
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M97.5,210.5L56.25,215c-1.5-8.414-2.25-16.43-2.25-24.039 c0-23.313,4.664-43.594,14.008-60.836s23.734-31.492,43.172-42.742S152.227,70.5,175.992,70.5 c21.836,0,41.578,4.891,59.234,14.656c17.656,9.773,30.945,22.414, 39.875,37.914c8.93,15.508,13.398,31.82,13.398,48.938 c0,17.281-3.164,33.438-9.492,48.453c-7.016,16.797-18.461,33.031-34.344,48.695c-15.883,15.672-47.891,40.781-96.016,75.344 h69.313c11.281,0,18.43-0.68,21.461-2.047c3.023-1.367,5.633-4.063,7.836-8.086s4.055-10.945,5.57-20.766l1.648-11.102H294.5 l-18.734,124h-27.18l-2.469-12H56.5v-46.766c27.875-21.32,63.242-52.82,106.086-94.5c21.969-21.32,36.602-39.734,43.883-55.242 c5.352-11.313,8.031-23.188,8.031-35.625c0-14.375-5.063-26.523-15.188-36.461S175.648,131,158.703,131 c-12.539,0-23.422,2.711-32.656,8.125s-16.461,13.133-21.695,23.148S96.5,182.875,96.5,194.016 C96.5,198.063,96.828,203.555,97.5,210.5z” />
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M1370.5,1842.5l41.25-4.5c1.5,8.414,2.25,16.43,2.25,24.039 c0,23.313-4.664,43.594-14.008,60.836s-23.734,31.492-43.172,42.742s-41.047,16.883-64.813,16.883 c-21.836,0-41.578-4.891-59.234-14.656c-17.656-9.773-30.945-22.414-39.875-37.914c-8.93-15.508-13.398-31.82-13.398-48.938 c0-17.281,3.164-33.438,9.492-48.453c7.016-16.797,18.461-33.031,34.344-48.695c15.883-15.672,47.891-40.781,96.016-75.344 h-69.313c-11.281,0-18.43,0.68-21.461,2.047c-3.023,1.367-5.633,4.063-7.836,8.086s-4.055,10.945-5.57,20.766l-1.648,11.102 H1173.5l18.734-124h27.18l2.469,12H1411.5v46.766c-27.875,21.32-63.242,52.82-106.086,94.5 c-21.969,21.32-36.602,39.734-43.883,55.242c-5.352,11.313-8.031,23.188-8.031,35.625c0,14.375,5.063,26.523,15.188,36.461 s23.664,14.906,40.609,14.906c12.539,0,23.422-2.711,32.656-8.125s16.461-13.133,21.695-23.148s7.852-20.602,7.852-31.742 C1371.5,1854.938,1371.172,1849.445,1370.5,1842.5z” />
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M675.605,575.327c-2.534-5.338-6.6-13.199-7.8-18.6c-3.601-13.8-7.2-30-3-44.4 c6.6-22.799,34.8-31.199,55.8-22.199c9,3.6,13.8,13.199,16.2,22.8c0.6,1.2,2.399,1.2,3,0c1.8-12.601,10.799-23.399,22.799-25.8 c24-4.2,44.4,13.199,47.4,36c2.399,19.199-4.199,37.199-13.799,54c-18.602,33.6-37.801,64.8-58.2,96.6 C714.005,643.728,692.405,610.728,675.605,575.327z” />
     <GeometryDrawing Brush=”#FF0000″ Geometry=”M801.395,1432.672c2.533,5.338,6.6,13.199,7.801,18.6c3.6,13.8,7.199,30,3,44.4 c-6.6,22.799-34.801,31.199-55.801,22.199c-9-3.6-13.799-13.199-16.199-22.8c-0.6-1.2-2.4-1.2-3,0 c-1.8,12.601-10.799,23.399-22.799,25.8c-24,4.2-44.4-13.199-47.4-36c-2.4-19.199,4.199-37.199,13.799-54 c18.602-33.6,37.801-64.799,58.2-96.6C762.994,1364.271,784.596,1397.271,801.395,1432.672z” />

In case you’re wondering, I didn’t create that XAML by hand! 🙂  I started with Adobe Illustrator, saved the file as SVG, then dealt with it from there.

The button style also contains an event trigger for Mouse.MouseEnter and another for Mouse.MouseLeave. Each of these points to a “storyboard” that handles the card animations (such as scaling and rotating).  The only piece of code I needed to write was the property that sets the face:

 // The user (via code or XAML) can simply specify the
 // name of the face, which must match the name of one of the
 // DrawingBrushes in Card.xaml.

 public string Face
   get { return face.ToString(); }
     // Let exception happen on bad input. The system handles it well.
     face = value;
     CardButton.Background = (
 private string face;

For now, the Hand control in Hand.xaml just hardcodes a set of cards in specific positions and rotations on a Canvas (again wrapped in a Viewbox for uniform scaling). For example:

 <c:Card Width=”100″ Canvas.Left=”10″ Face=”C7″>
   <RotateTransform Center=”50,140″ Angle=”310″ />

Enjoy, and don’t hesitate to give me feedback!

Comments (37)

  1. ShadowChaser says:

    Wow… awesome stuff 🙂

    Please tell me these are going to ship with Longhorn and not as a separate powertoy 😀

    I wonder how hard it would be to make a replacement for cards.dll. Does XAML support any type of inheritance or reusability?

  2. Adam Nathan says:

    Thanks! To be clear, this is just a demonstration for the sake of developer education. I don’t currently know what the plans are for the card games shipping in future versions of Windows.

    As for your question (ShadowChaser), there’s not much benefit in creating an Avalon-based binary-compatible replacement for cards.dll. But you could reuse the controls defined in my assembly in multiple applications. I haven’t looked into how easy or hard this is currently, but hopefully we’ll learn something as I do this with Internet Hearts!

  3. &lt;p&gt;&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://fb2.hu/x10/Articles/MonoForFun.html&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;Mono Mania&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://www.imation.com.au/products/disc_stakka/view_demo.htm&amp;quot; target=&amp;quot

  4. Mike Dimmick says:

    So, are you collaborating with Chris Sells on his plans for Longhorn Solitaire?

  5. Adam Nathan’s Win32 to WinFX Blog has a good post titled XAML Playing Cards. Adam takes the playing cards…

  6. Adam Nathan says:

    This is a separate effort, although I welcome Chris (or anyone else) to use my cards!

  7. Cool demo, Adam. You mentioned that you drew the artwork in Illustrator, then exported to SVG, and "dealt with it from there." What types of conversion operations are needed to create an XAML document from SVG data? I’m just starting to read about XAML and all of its potential, so apologies if this is an obvious question.

  8. William Luu says:

    Mike, to convert SVG to XAML have a look at:



    Adam, those cards look really good.

  9. Thanks for the links, William!

  10. In my previous XAML-related post, Mike asked about my conversion of XAML from SVG.&amp;nbsp; I know of two…

  11. DarkByte says:

    I have to admit, this is an awesome sample.

    a simple thing:

    I’ve been getting your sample code on XamlShare, tried to integrate it into a sample library that i created for learning and the PlayingCardHand class simply wont compile. The system refuse to accept Xaml for a class subclassing Viewbox. In the demo here, you provide Grid as a subclass.

    Is this a limitation of the compiler ?

    Will we be able to create custom controls using any base class in the future ?

  12. Adam Nathan says:

    Ah, it appears that the version I put on XAMLshare is actually for the next Avalon CTP. I’ve updated it to wrap the Viewbox in a Grid. But yes, that limitation will be removed very shortly!


  13. I’ve been reading a couple of Avalon blogs lately, considering to write my HeroQuest programs in Avalon…

  14. Anonymous says:

    Getting an error when trying to build

    A project with an Output Type of Class Library cannot be started directly.

    In order to debug this project, add an executable project to this solution which references the library project. Set the Executable project as the startup project.

    How do i resolve this issue?

  15. Adam Nathan says:

    You can right-click on the "Demo" project and select "Set as StartUp Project". Then you should be able to launch the app. That setting is stored in the .user file, which seemed wrong to distribute, although I should have just listed the Demo project first in the solution since that’s the one VS tries to start up by default.

  16. Trying to run your soft (with May RC1 release), I had the following error (with Visual Studio 2005 Beta 2):

    System.Resources.MissingManifestResourceException was unhandled

    Message="Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "Demo.g.resources" was correctly embedded or linked into assembly "Demo" at compile time, or that all the satellite assemblies required are loadable and fully signed."



    at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)

    at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)

    at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)

    at System.Resources.ResourceManager.GetObject(String name, CultureInfo culture, Boolean wrapUnmanagedMemStream)

    at System.Resources.ResourceManager.GetStream(String name, CultureInfo culture)

    at Demo.MyApp._InitializeThis() in C:Documents and SettingsGillesBureauCardsBeta1RCDemoobjDebugMyApp.g.cs:line 38

    at BootStrapper.EntryPoint.Main(String[] args) in C:Documents and SettingsGillesBureauCardsBeta1RCDemoobjDebugDemo.main.g.cs:line 61


  17. After installing Windows Vista and revealing that it has no significant dependency on WinFX or the .NET…

  18. I totally agree with what you’re saying. I wish more people felt this way and took the time to express themselves. Keep up the great work.

    Frank Gonzalez


  19. This blog posting was of great use in learning new information and also in exchanging our views. Thank you.

    Mary Anne Martin


  20. This blog posting was of great use in learning new information and also in exchanging our views. Thank you.

    Mary Anne Martin


  21. abhinandan says:

    longues peines dans des exils en margeant

  22. After installing Windows Vista and revealing that it has no significant dependency on WinFX or the .NET

  23. I got an email the other day that I thought I’d share (with permission from the author and with names