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">
  <DrawingBrush.Drawing>
   <DrawingGroup>
    <DrawingGroup.Children>
     <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>
     <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>
     <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" />
    </DrawingGroup.Children>
   </DrawingGroup>
  </DrawingBrush.Drawing>
 </DrawingBrush>

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(); }
   set
{
     // Let exception happen on bad input. The system handles it well.
     face = value;
CardButton.Background = (Brush)Resources[face];
   }
 }
 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" >
  <c:Card.RenderTransform>
   <RotateTransform Center="50,140" Angle="310" />
  </c:Card.RenderTransform>
 < /c:Card>

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

Comments

  • Anonymous
    May 03, 2005
    Wow... awesome stuff :-)

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

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

  • Anonymous
    May 03, 2005
    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!

  • Anonymous
    May 04, 2005
    &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

  • Anonymous
    May 04, 2005
    So, are you collaborating with Chris Sells on his plans for Longhorn Solitaire?

  • Anonymous
    May 04, 2005
    Adam Nathan's Win32 to WinFX Blog has a good post titled XAML Playing Cards. Adam takes the playing cards...

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

  • Anonymous
    May 06, 2005
    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.

  • Anonymous
    May 08, 2005
    Mike, to convert SVG to XAML have a look at:
    http://www.xamlon.com/kb/article.aspx?id=10018
    http://workspaces.gotdotnet.com/svg2xaml

    Adam, those cards look really good.

  • Anonymous
    May 10, 2005
    Thanks for the links, William!

  • Anonymous
    May 11, 2005
    In my previous XAML-related post, Mike asked about my conversion of XAML from SVG.&amp;nbsp; I know of two...

  • Anonymous
    May 16, 2005
    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 ?

  • Anonymous
    May 18, 2005
    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!

    Thanks!

  • Anonymous
    May 23, 2005
    I've posted a Beta 1 RC version of the sample at http://www.pinvoke.net/blog/downloads/CardsBeta1RC.zip.

  • Anonymous
    May 30, 2005
    I've been reading a couple of Avalon blogs lately, considering to write my HeroQuest programs in Avalon...

  • Anonymous
    June 02, 2005
    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?

  • Anonymous
    June 02, 2005
    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.

  • Anonymous
    June 13, 2005
    The comment has been removed

  • Anonymous
    June 15, 2005
    weight loss pill http://www.nofatonline.com/

  • Anonymous
    July 28, 2005
    After installing Windows Vista and revealing that it has no significant dependency on WinFX or the .NET...

  • Anonymous
    February 08, 2006
    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
    http://www.postcardmaniafun.com

  • Anonymous
    May 04, 2006
    This blog posting was of great use in learning new information and also in exchanging our views. Thank you.
    Mary Anne Martin
    http://www.postcardmaniafun.com

  • Anonymous
    May 04, 2006
    This blog posting was of great use in learning new information and also in exchanging our views. Thank you.
    Mary Anne Martin
    http://www.postcardmaniafun.com

  • Anonymous
    July 28, 2006
    dicover credit card <a href=http://dicovercreditcard.artshost.com/>dicover credit card</a>

  • Anonymous
    July 28, 2006
    dicover credit card <a href=http://dicovercreditcard.isportsdot.com/>dicover credit card</a>

  • Anonymous
    July 31, 2006
    longues peines dans des exils en margeant

  • Anonymous
    August 09, 2006
    cardgames <a href=http://cardgames.noneto.com>cardgames</a>

  • Anonymous
    August 30, 2006
    After installing Windows Vista and revealing that it has no significant dependency on WinFX or the .NET

  • Anonymous
    December 04, 2006
    I got an email the other day that I thought I'd share (with permission from the author and with names

  • Anonymous
    June 01, 2009
    PingBack from http://uniformstores.info/story.php?id=15746

  • Anonymous
    June 02, 2009
    PingBack from http://woodtvstand.info/story.php?id=89451

  • Anonymous
    June 09, 2009
    PingBack from http://greenteafatburner.info/story.php?id=397

  • Anonymous
    June 16, 2009
    PingBack from http://fixmycrediteasily.info/story.php?id=1860