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
<p>&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://fb2.hu/x10/Articles/MonoForFun.html&quot; target=&quot;_blank&quot;&gt;Mono Mania&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.imation.com.au/products/disc_stakka/view_demo.htm&quot; target=&quotAnonymous
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.&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 removedAnonymous
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.comAnonymous
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.comAnonymous
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.comAnonymous
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 margeantAnonymous
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 .NETAnonymous
December 04, 2006
I got an email the other day that I thought I'd share (with permission from the author and with namesAnonymous
June 01, 2009
PingBack from http://uniformstores.info/story.php?id=15746Anonymous
June 02, 2009
PingBack from http://woodtvstand.info/story.php?id=89451Anonymous
June 09, 2009
PingBack from http://greenteafatburner.info/story.php?id=397Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=1860