I am a great fan of Silverlight. I have never liked 1.0, but when version 2.0 was released I was amazed. I created a few solitaire games. Now I decided to create a new game and to describe the problems I have encountered to you. First I decided to write an article about creating a Silverlight application for Facebook. But subsequently I decided to divide the article in two parts – creating a Silverlight application, which is a game in this occasion, and integrating it in Facebook.
I am going to create a game, called Cows & Bulls. You play that game for time. It supports a list of all players and their time.
Beginning
I will use a strong pattern in my application, so I need to put the common styles in the application resources. Now I can access them from everywhere in my application.
1 2 3 4 5 6 |
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="CowsAndBulls.App"> <Application.Resources> </Application.Resources> </Application> |
Game Structure
The structure of my game looks like this (the arrows represent the possible transitions between the pages). I am going to describe you the most interesting parts of each page; what kind of problems I have encountered and how I have solved them.
Home Page
It contains nothing more than a list of links to other pages. In this application I have changed the default style of the ListBox control in order to adapt it for my need. How is this achieved? You have to change the Template of the control. You can read my article for more information.
1 2 3 4 5 6 7 8 9 10 11 |
<Style TargetType="ListBox"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBox"> <Grid> <ItemsPresenter /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> |
As ListBox is an item control it needs ItemsPresenter to represent its items. When you have predefined the default style of the ListBox, you need to predefine the default style of the ListBoxItem, too.
Game Page
The game page contains three major controls:
- Game Grid
- Timer
- Animated Assistant
I needed something like a ListBox, but I wanted one ListBoxItem to contain three subitems. DataGrid is very good for this purpose, because it supports columns. But it would make my application bigger and slower, because it has many other properties, which I do not need in this case. That’s why I decided to create my own control. It is nothing more than a predefined ListBox, of course. Here is the ControlTemplate for the ListBoxItem.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<ControlTemplate TargetType="ListBoxItem"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Path=Number}" TextAlignment="Center" /> <TextBlock Grid.Column="1" Text="{Binding Path=Cows}" TextAlignment="Center" /> <TextBlock Grid.Column="2" Text="{Binding Path=Bulls}" TextAlignment="Center" /> </Grid> </ControlTemplate> |
Data binding is one of the most fundamental techniques in WPF and Silverlight as well. I have a class, called Player, which represents the player’s details. When I change the DataContext of my control, each TextBlock immediately “gets” the value of the property it is bound to.
To compare the players, I need to know the exact time, each has completed the game. That’s why I have created a simple timer. It uses a DispatcherTimer. It is a timer that is integrated into the Dispatcher queue which is processed at a specified interval of time and at a specified priority. Timers are not guaranteed to execute exactly when the time interval occurs, but are guaranteed not to execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes, it is dependent of the other jobs in the queue and their priorities.
If a Timer is used in a WPF application, it is worth noting that the Timer runs on a different thread then the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. Reasons for using a DispatcherTimer opposed to a Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DispatcherTimer timer; long seconds; public Clock() { this.timer = new DispatcherTimer(); this.timer.Interval = TimeSpan.FromSeconds(0.999); this.timer.Tick += new EventHandler(timer_Tick); } private void timer_Tick(object sender, EventArgs e) { TimeSpan diff = TimeSpan.FromSeconds(++this.seconds); } |
As you can see, I have set the interval of the timer to 0.999, instead of setting it to 1. This is because the timer needs a little time to execute the code in timer_Tick method. Well, the timer is not the perfect one, but it simulates a real chronometer.
I have added a simple animated assistant, which “tells” the player some things. It moves his eyes up and down. It uses two DoubleAnimations to achieve this. Pay attention that when you want to change an attached property, you have to surround it with rounded brackets.
1 2 3 4 |
<Storyboard x:Name="EyeAnimation"> <DoubleAnimation Storyboard.TargetName="LeftEye" Storyboard.TargetProperty="(Canvas.Top)" RepeatBehavior="Forever" From="30" To="40" Duration="0:0:4" AutoReverse="True" /> <DoubleAnimation Storyboard.TargetName="RightEye" Storyboard.TargetProperty="(Canvas.Top)" RepeatBehavior="Forever" From="30" To="20" Duration="0:0:4" AutoReverse="True" /> </Storyboard> |
DoubleAnimation is very powerful when you want to change the value of property of an object bit by bit. Setting AutoReverse to true reverses the animation and you see an endless (when you set RepeatBehavior to forever) fluent animation.
Congratulations Page
It is shown when a player completes the game. Here it writes his name. His details are then send to the Statistics Page, where they are send to the server.
Statistics Page
This page show the top players, which have the best time in completing the game. In this page we made a web request to send player’s details and get the top players. Update method, shwon above on the Player class diagram processes a web reuqest through a WebClient. This class provides common methods for sending data to and receiving data from a resource identified by a URI. Furthermore it processes all request in another thread. When you make calls to web service or other time-taking resources, it is very essential to put the code in another thread. However, if you need more specific options when communicating with a web server, you have to use HttpWebRequest. It provides support for additional properties and methods that enable the user to interact directly with servers using HTTP.
The following code posts player’s details to the web server. In this case I have created a simple PHP file and put it on my own server. The web client does request to this file, which manipulates the data.
1 2 3 4 5 6 7 |
WebClient client = new WebClient(); string postData = "Name=PlayerName&Time=00:02:43"; client.Encoding = System.Text.Encoding.UTF8; client.Headers["Content-type"] = "application/x-www-form-urlencoded"; client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted); client.UploadStringAsync(new Uri("http://somewebsite.com/somefile.php"), "POST", postData); |
The default encoding for all forms is application/x-www-form-urlencoded. A form data set is represented as follows:
- The form field names and values are escaped: space characters are replaced by ‘+’, and then reserved characters are escaped
- The fields are listed in the order they appear in the document with the name separated from the value by ‘=’ and the pairs separated from each other by ‘&’
You can read more about managing form data in the W3 website.
When you make request to a web server you may encounter a cross-domain policy problem. You can read more about this in Karen’s blog. In Silverlight, we send cookies & authentication with each cross domain request. Because of this, web service authors need to be careful to separate their 3rd party accessible APIs from their main site. You need to configure a file, called clientaccesspolicy.xml, in order to set the permissions for calls from Silverlight applications. It looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="Content-Type"> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy> |
When player’s details are successfully saved, we have to get the top players list.
1 2 3 |
webClient.Encoding = System.Text.Encoding.UTF8; webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted); webClient.DownloadStringAsync(new Uri("http://somewebsite.com/somefile.php")); |
DownloadStringCompletedEventArgs contains a property, called Result. It returns the result from the server as a string. My PHP file returns the result in XML format. Now I can use LINQ to XML to obtain the players’ details. LINQ to XML is very powerful technique and it’s so great that Silverlight 2 supports it. You need to add a reference to System.Xml.Linq in your project in order to use it. And here it is:
1 2 3 4 5 6 7 8 9 |
XDocument doc = XDocument.Parse(e.Result); var players = from player in doc.Descendants("Player") select new Player { Number = int.Parse(player.Attribute("No").Value), Name = player.Element("Name").Value, Time = player.Element("Time").Value }; |
Then I just set the ItemsSource of my ListBox control to players and they show up.
Conclusion
Creating games with Silverlight is so easy and pleasant. I have uploaded this game on my web site, so you can try to beat it. You can view its source and post comments on it. Have fun!
References
- http://mark-dot-net.blogspot.com/2008/04/styling-listbox-in-silverlight-part-1.html
- http://venugopal-wpfandxaml.blogspot.com/2008/06/what-is-difference-between-timer-and.html
- http://www.w3.org/MarkUp/html-spec/html-spec_8.html
- http://scorbs.com/2008/04/22/silverlight-http-networking-stack-part-3-configuring-a-cross-domain-policy-file/
- http://msdn.microsoft.com/en-us/library/bb308960.aspx
[ Application Source ] | [ Application on the Web ]