April 2008 - Posts

Two weeks ago, I posted about my Solitaire program with improved WPF animations. I'm still working on code cleanup to release the source, but I'd like to document some cool tricks I learned about Borders that might come in handy.

(As an aside, I said I had an interview. Well, after two cross-country flights, and two grueling rounds of interviews I've been offered a job which I'm going to accept. I'll talk more about that when I can)

All of the cards used in the WPF version of this program were from OpenClipart, including the card backs. I've no problem with the card faces, but I wanted to include several different backs, including the ability to import custom ones. There were a couple of challenges with this. I wanted a rounded corners, a drop shadow, and the ability for the picture to fill the entire inside of the border with it's rounded edges.

The rounded corners were easy enough, just use the CornerRadius property.

<Canvas x:Name="mainCanvas" Background="Green" Width="250" Height="250">
    <Border BorderBrush="White" BorderThickness="5" CornerRadius="15">
    </Border>
</Canvas>


sample1

The drop shadow proved to be a little more difficult, since WPF doesn't provide 3D Borders by default. However, a little searching turned up a method to create the a 3d look for square borders by nesting two borders.  Some experimentation, and I found this would work quite well for a drop shadow.

<Canvas x:Name="mainCanvas" Background="Green" Width="250" Height="250">
    <Border BorderBrush="DarkGray" BorderThickness=".0,.0,2,2" CornerRadius="18">
        <Border BorderBrush="White" BorderThickness="5" CornerRadius="15" >
    </Border>
</Border>
</Canvas>

sample2

Note that you have to set the CornerRadius on the outer Border to a slightly larger value or you get a gap between the outer and the inner border.

Now comes the hard part, getting the image inside the border.

I used the following image, and dropped the <image> tag right into the inner border.

happy sample3

As you can see, it doesn't completely fill the border AND the square corners of the image overlap the rounded corners of the border. Not good. I turns out one more trick is all that's necessary. Instead of putting the image inside the border, you make the image the border of the background. Also, a third nested border gives the black border around the image to give some demarcation to the inner image, and completes the card.

<Canvas x:Name="mainCanvas" Background="Green" Width="250" Height="250">
    <Border BorderBrush="DarkGray" BorderThickness=".0,.0,2,2" CornerRadius="18"
    Height="190" Width="140" Canvas.Top="30" Canvas.Left="55"
    >
        <Border BorderBrush="White" BorderThickness="5" CornerRadius="15" >
            <Border BorderBrush="Black" BorderThickness="1.5" CornerRadius="12">
                <Border.Background>
                <ImageBrush>
                    <ImageBrush.ImageSource>
                        <BitmapImage UriSource="c:\temp\happy.jpg" />
                    </ImageBrush.ImageSource>
                </ImageBrush>
            </Border.Background>
            </Border>
        </Border>
</Border>
</Canvas>

sample4

So there you have it, all the steps to make a card, and hopefully some tricks to add to your bag of tricks.

I've been working on a Solitaire program on and off for years (pretty much since .NET 1.0 was in beta) The back-end engine/framework was pretty straightforward, but I could never get the animation quite right. I've kept it up-to-date with each release of the framework, updating for generics, and even trying DirectX, but I could never get it quite right.

When WPF came along and I came across some SVG Clipart for the cards, I was inspired to try again. This time around, the cards looked pretty good, and the animations (when they displayed were very smooth) but I kept having trouble with threading, so when the deck was shuffled, it only showed one card move, and that was the last one! Frustrating, to say the least, to come so far, then get hung up!

However, this week, when working on a Silverlight project, I relearned how to do animations, particularly in code, and finally (FINALLY!), I've gotten it so that it draws very smoothly, animates all actions and actually looks pretty good. (IMHO)

I'm actually amazed by how much I gained by going to Storyboards for the motions. Instead of having to fuss over each animation, and try to wait while it was done, I could do entire sets of moves with a ParallelTimeline so that a while stack of cards could move in tandem without any real manipulation necessary. The initial deal, or a pull from the deck looks like all the cards are being carelessly thrown out, but they all land exactly where they need to go. And it was all done in at least 50% less animation code than any other version I've tried in the past.

Of course, there's a lot of other stuff to fuss with, like better mouse handling and memory usage and more variants, but I'm really pleased.

Also, the rules code is almost completely abstracted from the animation code, so I think it could even be ported to Silverlight or XNA with minimal fuss.

Mind you, not tonight <grin>

At any rate, here's the current install: Solitaire

I'm going to clean up the source and release it shortly, but I've got an interview coming up this week that I want to prepare for. Wish me luck.

More Posts