Published: 05 Feb 2010
By: Xianzhong Zhu
Download Sample Code

In the two parts of this series, you will learn to write a Silverlight 3 based solitaire game like the one shipped with various Windows versions. The main purpose to write this game is to explore the mouse drag and drop related programming in Siverlight 3.

 You can view the live demo here.

Contents [hide]

Cloning Windows Solitaire Game in Silverlight 3 Series

  • Part 1 In the two parts of this series, you will learn to write a Silverlight 3 based solitaire game like the one shipped with various Windows versions. The main purpose to write this game is to explore the mouse drag and drop related programming in Siverlight 3.
  • Part 2 In this second part of the series, you will learn the crucial programming skills of the solitaire game. In detail, we are going to discuss about the three mouse events, i.e. MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp, related event handler programming.
  • Introduction

    You readers are sure to be familiar with the Windows solitaire game. Nowadays, with Silverlight becoming more and more heated, can we clone the Windows solitaire game using Silverlight 3? Yes, of course. In this series of articles, we are going to rebuild the solitaire game with the main purpose of learning the basic mouse drag and drop operations in Silverlight 3. And also, with the interesting story going on, you will learn many other fundamentals and skills in developing basic Silverlight applications. With another main purpose of writing a basic Silverlight tutorial, I'll try to explain some of the related concepts as thoroughly as possible.

    In this first article, we will mainly dwell upon the fundamentals in developing the Silverlight based solitaire game. After that, we will discuss the solitaire game rules, and, at the same time, the important data structures and related basic code are also taken apart.

    NOTE

    The development environments in the solitaire game application are as follows:

    Windows XP Professional (SP3);

    .NET 3.5 (SP1);

    Visual Studio 2008 Professional (SP1);

    Microsoft Expression Blend 3;

    Microsoft Silverlight Tools for Visual Studio 2008 SP1;

    Our Own Mouse Drag & Drop Solution

    To be honest, at the beginning of developing the solitaire game, I've nearly sought through the entire Internet to try to find some suitable Silverlight based drag and drop solutions. However, I found no proper ones to be quickly put into use in the solitaire game. To find a appropriate solution to manage the drag and drop as well as double-click operations, I've studied many close solutions, such as the simplest drag and drop solution, the TranslateTransform solution, the Expression Blend 3 MouseDragElementBehavior solution, the Flex like DragManager class solution, the codeplex provided Silverlight Drag Drop Manager, the MouseClickManager solution, and all other possible ones. Finally, I decided upon the basic drag and drop solution, which is the fussiest one and, at the same time, the most flexible one.

    In the subsequent sections, we are going to introduce the important concepts related to our own mouse drag and drop and double click solution.

    Dealing with the Card Coordinate

    For various container controls, such as Grid, Canvas, Stack, etc., there are different kinds of coordinate managing solutions. In this solitaire game, to simplify the design we select to use the Canvas control to serve as the container control of all the card controls. Therefore, we can fall back on the following functions to deal with the card coordinates:

    Listing 1: The important card coordinates related functions

    In our game, we directly use the methods GetLeft and GetTop to get the upper left coordinates of the card controls. Note here these coordinates are relative to the parent Canvas. On the other hand, we resort to another two methods SetLeft and SetTop to specify the upper left coordinates of the card controls, so that we can move the cards on the fly on the canvas.

    Dealing with the Card zIndex

    Another interesting and important thing worth noticing is the z index of each card. As we know, by default, all the sub controls in a parent control have the same zIndex property value, i.e. 0. However, due to the characteristics of Silverlight design, the controls with the same zIndex property value are rendered on the screen in 'different layers' (if positions overlap, the later rendered one will seem on top of the one done earlier).

    On the other hand, when we drag the card(s), the dragged cards should seem to be moving over all the other cards. So, we have to intervene in the zIndex property value of the dragged cards. Further, considering that there are totally 52 cards and the valid value range for the zIndex property is enough for our game, we can take the action to add 1 to the zIndex property value of the current dragging card each time the card is being dragged. If the card is droppable to the target position, then the new zIndex property value will not be changed; otherwise, we need to restore the zIndex property value of the current dragged card to its old value, so that it seems all the relative positions and layers have not changed.

    As depicted above, we are still to list the mainly-used methods to control the zIndex property value of the cards.

    Listing 2: The methods to control card z-index

    The usage of the above two methods here is almost the same as the coordinate related ones discussed previously. Additionally, you may set negative value to the zIndex property for special controls on the canvas if needed.

    About Silverlight 3 Double Click Support

    The mouse left button double click (referred to later as double click) is also needed in the solitaire game, such as in the case that when there are a face-up card at the topmost of the deck or row stacks and there is a matched card at the suit stack area by double clicking the current card we can move it to the suit stack more quickly.

    As is known, Silverlight currently has full mouse support for single click. However, it does not provide direct double click support though we can achieve this indirectly. To do this, many blog articles show their respective solutions. In this game, I have utilized the solution provided by Mike Snow. His method is easy to understand, the key of which is to start a DispatcherTimer timer once a left mouse click event has been received. If another mouse click is intercepted before the double click time interval has passed then a double click has occurred. This interval is typically set to be around 200 milliseconds. Once 200 milliseconds have passed the timer is stopped and disabled until another mouse click is received.

    You should also notice that the above-mentioned MouseClickManager solution is a typical and a good solution under simple cases that merely require us to distinguish the mouse click from double-click operations. However, in our case, it's not suitable since the solitaire game is stricter requiring us to keep track of the mouse button down, mouse move, and button up actions, as well as to distinguish the single mouse button click from the double click.

    NOTE

    Though many known people suggested the 200 milliseconds double click solution, I have not gained satisfactory result in the solitaire game. And also, I've tested another two number-- 300 and 400, but still resulting in a pessimistic story-- sometimes you may have to double click your left mouse button three times to gain the general double-click effect. In testing the game, if you feel the double click is occasionally out of effect you can change to the dragging way. If you find better solutions, please feel free to share with the others.

    Next, let's look at the custom double click related solution in Silverlight 3, as shown in Listing 3 below.

    Listing 3: Creating a DispatcherTimer for mouse double click detecting

    To start, let's create a DispatcherTimer named doubleclickTimer and add a listener for the Tick event. Here, note that by invoking the method Start the timer doubleclickTimer is started, and the value of the property IsEnabled becomes true accordingly. Apparently, at the very beginning, the value of the property IsEnabled is false.

    If another mouse click is intercepted before the double click time interval (set to 200 milliseconds in our case) has passed then a double click has occurred. Once 200 milliseconds have passed the Tick event will be triggered, and the timer is stopped and disabled until another mouse click is received. So, the comment-hinted positions in the above code we can insert our custom double click and single click programming logics, respectively.

    Capturing Mouse Actions and Subscribing to Related Events

    How to set mouse capture is the first puzzle coming into my mind in thinking of developing the solitaire game. Will I set mouse capture for each card, only the current card, or the parent Canvas control? At last, I decide to subscribe to the mouse related events for the parent Canvas control (named cardContainer) while I set mouse capture merely for the current card. In this way, we can easily obtain all required info about the current card to move it, and accordingly, get all other possible cards related info to achieve the result of moving them together. Since this is a lengthy story, we are going to explore all the three mouse events, i.e. MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp, related programming in the second article.

    About the Silverlight Menu Control

    To research into the Silverlight menu related topic, I purposefully introduced the Codeplex supported Silverlight Menu Control to control the overall logic of the game. Although to use this menu control is a light-hearted story, there are still at least two points deserved your care. One is you should change the zIndex property value to a large integer (1000 is enough in our case), the other may be a small bug of the current release version - when you click a menu item you have to click other areas out of the menu's scope, then you can begin your real work.

    In fact, there is also a third and most important point you should take notice of. Let's leave it to the later section.

    Playing the Solitaire Game

    First of all, let's take a look at the playing rules of the solitaire game. With these rules in mind, we can find the better solutions in controlling the mouse dragging and dropping. As you've known, the object of Solitaire is to use all the cards in the deck to build up the four suit stacks in ascending order, beginning with the aces. Figure 1 gives one of the running-time snapshots of this game.

    Figure 1: One of the running-time snapshots of the solitaire game

    One of the running-time snapshots of the solitaire game

    I believe most readers are quite familiar with the Windows versioned solitaire game. Therefore, next, we'll enumerate the all the possible valid drag and drop operations in this game.

    First, you can click anywhere in this game view. However, if no card is clicked or you click a face-up card (whether it's at the deck, at the row stacks, or at the suit stack), nothing will happen. However, if you click a face-down card, whether it's at the upper left board area or at the bottom row stack area the card will be turned (flipped). Note in the figure the valid areas for single click is marked with a number in a red circle.

    Figure 2: All the valid single click areas in the solitaire game

    All the valid single click areas in the solitaire game

    Next, let's consider the valid double-click areas. As the game rules indicate, by double clicking a face-up card, whether it's at the deck or at one of the row stacks, if the other conditions are met the card will automatically fly to one of the proper suit stacks at the upper right area. After the current card flies to the target area, the next possibly existing card (just below the current card) will appear with face downward, ready to be turned next time.

    Last, let's examine the drag and move case. Only when the current card is face up, can it be dragged over other cards. And further, if the other valid conditions are met, the current card can be dropped onto the target card at one of the suit stacks. A general case is to drag a pile of face-up cards, which will be discussed in detail in the second article.

    Designing the Game

    With the game rules in mind, we are first going to explore the elementary programming. To achieve a better understanding, let's first look at the different partitions on the game screen, as shown in Figure 3.

    Figure 3: The different partitions sketch map in the solitaire game

    The different partitions sketch map in the solitaire game

    Note all the names in Figure 3 refer to the original addressing of the Windows-versioned solitaire game. For simplicity, we only list the most important data structures used in our game, as below.

    Listing 4: The crucial data structures used in our game

    In the above code, the variable CurrentCard is used to hold the current card. Whenever you click any one of the cards on the screen, the value of the variable is decided.

    Next, the variable Board is used to hold the random twenty-four cards. To facilitate the card flipping and counting, we've introduced a special 'empty' card which will always be the last one in the list. The second variable Deck is used to flip the card(s). In our case, as that implemented in the Windows version, you can select to flip one or three cards each time through the Options dialog in the source code. The rest two variables SuitStack and RowStack are both generic list arrays, used to hold the cards at the suit stack and the row stack areas, respectively.

    To facilitate measuring the positions of the cards, we've also introduced a placeholder list called PlaceHolder. Note there are totally thirteen placeholders defined in our case, which will be initialized in the helper method InitPlaceHolder during the initialization of the game. Listing 5 gives the related code.

    Listing 5: The card placeholders initialization

    As you can also see from the above figure, there are totally thirteen card placeholders in the main game view. Therefore, we defined an array of List<Rect> with 13 elements. Note some of the rectangle placeholders are with different sizes, which is easy to understand if you are familiar with the game.

    Defining the Card Control

    In preparing for the game, what comes into my mind is define each card as an independent user control. Along with this idea, we need to define the related face, rank, and suit in the property forms. But, how to deal with the card related images is a question. You may have your own ideas. In my idea, the control maintains an Uri array, with each Uri pointing to the corresponding card image. Listing 6 below gives the definition of the Card control.

    Listing 6: The Card control definition

    Note herein we defined totally 55 images except for the red Joker and black Joker. The last three special images, as mentioned above, are for special use. To retrieve the relevant image, we defined an index pointer. And also, to facilitate the instantiation of the Card control, we overrode the constructor. As for the rest two methods, SetToFaceUp and SetToFaceDown, as the name hints, they are used to change the states of the related cards.

    Let's next look into how to generate and deal cards with the data structures defined above.

    Generating and Dealing Cards

    To start the game, you first have to deal the card, which is done in the method GenerateAndDealCards. Listing 7 below gives the first part of this method. First of all, you should generate 52 cards in random order.

    Listing 7: Generating 52 cards in random order

    Here, we define a Card array arrPoker to hold the final 52 cards in random order. Another Card array OrderPoker is used to store the ordered cards. With the help of the method RandomKDiffer(0, 51, 52, RandomI), we can generate 52 different cards with the index numbers ranging from 0 to 51. The last thing should be noticed is that the 52 cards stored in array arrPoker are all initialized with face downwards.

    For now, the required 52 cards are ready. Then, the next thing to do should be put them on the screen at valid places and in requested forms. Obviously, at the very beginning of the game, there are only two parts of places having cards, i.e. the upper left part (board) and the bottom part (row stack).

    Let's first look into the board area related initialization code:

    Listing 8: Initializing the board with random 24 cards

    According to the design of the initial Windows solitaire game, we should put 24 random cards at the upper left board area, with all faces downwards. Here, the control cardContainer is the parent container control of all the cards. And also, we directly modify the cards coordinates by calling the static methods SetLeft and SetTop of the Canvas control. As arranged above, the Rect PlaceHolder[0] represents the board area related rectangle area. The last point to be taken notice of is we purposefully design an empty card to indicate the end of the card turning action and specify it as the first Card typed child control. Since we add the empty card as the last node of the List<Card> structure Board, there are totally 25 cards in it, i.e. node Board[24] representing the empty card while the rest nodes Board[0~23] corresponding to the random 24 cards.

    Next, let's continue to see how to initialize the bottom row stack area with the rest random 28 cards, as illustrated in Listing 9 below.

    Listing 9: Initializing the row stack area with the rest random 28 cards

    In the above code, there are only two points needed to be pointed out:

    • The number of cards at bottom from left to right is 1, 2, 3, 4, 5, 6, and 7, respectively.
    • The uppermost cards in each stack of the bottom area are all face up, while all the rest cards in the row stack are face downwards.

    At last, there is still a tiny tip associated with the suit stack area. As with the upper left board area design, we've also add a special card (the placeholder card) at the bottommost of each suit stack. This is mainly to facilitate the control of the mouse click related programming. Figure 4 below shows the special placeholder cards at this area.

    Figure 4: The special placeholder cards at the suit stack area

    The special placeholder cards at the suit stack area

    The following code indicates how to add the four special cards to the suit stack area.

    Listing 10: Adding four special cards to the suit stack area

    Next, let's look at another tip required to develop the poker game.

    Using the VisualTreeHelper Class to Find the Current Card

    As you may image, we frequently need to locate the current card in developing the game application. Therefore, we create a special method named GetCurrentCard to achieve this target. The following short sentence shows the typical use of this method:

    In fact, this is also the most frequent way to find the current card. So, let's continue to follow up the scent to delve into this method.

    The following gives the complete implementation code of the method GetCurrentCard:

    Listing 11: The complete code for the method GetCurrentCard

    As is seen, the heart of the above code is the invocation of the static method FindElementsInHostCoordinates of the VisualTreeHelper class. Since Silverlight is mainly based upon the declarative XAML markup language, the VisualTreeHelper class is created to provide utility methods to help to traverse object relationships (along child object or parent object axes) in the Silverlight visual tree. Here, we used its member method FindElementsInHostCoordinates to retrieve the current card at the current mouse click position. By searching through the returned UIElements, we can easily find out the current clicked card. I highly recommended your readers to set a break point inside the method to explore all the related details (please also note the structure of the user control Card).

    Another important point you should pay attention to in the above code is, to get current mouse position, the argument passed to the method GetPosition is not cardContainer (the cards container) but null. However, the second argument passed to the method FindElementsInHostCoordinates is not null but cardContainer. Remember that in developing this game, if you do not pass arguments like these, you will find peculiar things to happen. Believe it or not; you can test it.

    If the current card is found, we return it to the caller; or else, just return null.

    Replaying the Game

    As an integrated game application, it should provide the support for replaying the game. As the name implies, before restarting the game we should reset all required data structures to their original values or states. It, in fact, is not as easy as it seems to have all data restored. On the one hand, the reset work relies upon what kinds of data structures you introduced; on the other hand, it depends on where you launch the resetting action.

    In our case, the resetting action is not difficult to perform. Thanks to the Silverlight menu control, all reset associated things get along as in your familiar desktop applications. Listing 12 below shows the related code.

    Listing 12: Code for starting or restarting the game

    By invoking the two helper methods ResetGameEnvironment and InitializeAndStartTheGame, everything will get ready for you to play the game. Here, we are to disregard the related discussions since they are all simple code.

    As mentioned earlier, there is an important question (in fact several) here: where to register the mouse events? In resetting the game data, do we also need to un-register these mouse events and register them again when we want to replay the game?

    Here, I leave these questions to you readers to further research into, and just show you the answer.

    It's in the Loaded event handler of the MainPage control that we invoked the helper method RegisterMouseEvents, as shown below.

    Listing 13: Code for registering the mouse events

    Another part of the above answer is we do not need to un-register the mouse events and register them again when we want to replay the game. If you put the invocation of the method RegisterMouseEvents in the helper method InitializeAndStartTheGame, you will find peculiar thing happens - the mouse double click and dragging support fail! Just try it to experience this.

    Summary

    Over many years, the desktop-level solitaire game shipped with various versions of Windows has been a well known game, which not only brings to people a great deal of enjoyment but also causes many of them to study its related programming tips and even mathematic algorithm. In this first part of the series, we've only touched upon the main techniques and sleights in developing the Silverlight based solitaire game. In the next part of the series, we are going to explore the most interesting and important part of the solitaire game, i.e. the three mouse events (MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp) related programming.

     You can view the live demo here.

    Cloning Windows Solitaire Game in Silverlight 3 Series

  • Part 1 In the two parts of this series, you will learn to write a Silverlight 3 based solitaire game like the one shipped with various Windows versions. The main purpose to write this game is to explore the mouse drag and drop related programming in Siverlight 3.
  • Part 2 In this second part of the series, you will learn the crucial programming skills of the solitaire game. In detail, we are going to discuss about the three mouse events, i.e. MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp, related event handler programming.
  • <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Xianzhong Zhu

    I'm a college teacher and also a freelance developer and writer from WeiFang China, with more than fourteen years of experience in design, and development of various kinds of products and applications on Windows platform. My expertise is in Visual C++/Basic/C#, SQL Server 2000/2005/2008, PHP+MyS...

    This author has published 78 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    Folder Dialog in Silverlight 4
    Diptimaya Patra discusses the Folder Dialog in Silverlight 4.
    Develop a Flexible 2.5D Scene Editor Targeting Silverlight RPG Games - Part 1
    Starting from this article, I'm going to introduce to you an excellent 2.5D RPG games scene editor -...
    Develop a Flexible 2.5D Scene Editor Targeting Silverlight RPG Games - Part 2
    In this article, I'm going to introduce to you how to construct such a 2.5D RPG game scene editor th...
    Displaying Notification Messages in a Silverlight Dashboard Application
    In this article we will see how we could display a notification message and further a list of notifi...
    Widget Refresh Timer in MVVM in Silverlight
    In this article we'll see how to refresh and disable widgets using the Model View View-Model pattern...

    You might also be interested in the following related blog posts


    Tech Ed 2006 Summary read more
    Web Parts in SUB V2 read more
    Apple Safari for Windows and Microsoft Silverlight read more
    Moonlight 1.0 Released, Silverlight script updated – and a Chrome hack read more
    Silverlight Controls read more
    Telerik Announces Support for Microsoft Silverlight 3 read more
    Why Embedded Silverlight Makes Sense read more
    Some Silverlight ecosystem updates read more
    Coding a Euchre Game, Part 7: Total Logic (Matt Gertz) read more
    Using XML as a resource in your code one more series on gamewriting (Matt Gertz) read more
    Top
     
     
     

    Discussion


    Subject Author Date
    placeholder Interesting, thanks Speednet . 2/6/2010 9:54 AM
    RE: Interesting, thanks Sonu Kapoor 2/9/2010 10:30 PM
    placeholder RE: Interesting, thanks Xianzhong Zhu 2/9/2010 8:53 AM
    Awesome! Elving Rodriguez 2/7/2010 11:41 PM
    placeholder RE: Awesome! Sonu Kapoor 2/9/2010 10:30 PM

    Please login to rate or to leave a comment.