Blog Archives

Handling Dynamic Storyboards and Animations in Silverlight 2

Animations are a key part of Silverlight 2 that allow your applications to stand out from all of the boring and dull applications floating around on the Web.  In previous articles I’ve written about storyboards and animations but as a quick review, Silverlight relies upon a Storyboard element to define several different types of animations such as DoubleAnimation (which animates object properties of type double) and ColorAnimation (which animates color properties).  In this article I’ll show you how Silverlight can be used to create animations programmatically and how you can interact with animations defined declaratively in a XAML file using C#.

For this example I’ll create a simple “lightbox” style container that can display pictures from Flickr.  As the picture is being shown the box will grow from a height/width of 0 to a larger size that’s determined by the size of the browser.  Three different animations will be performed by the image display container:

  • Animate the Width from 0 to the width of the browser / 1.5
  • Animate the Height from 0 to the height of the browser / 1.5
  • Animate the Opacity from 0 to 1

If you know the To and From values of the animations upfront then it’s easiest to define them in the XAML file.  In cases where there’s an unknown animation property value, you can create the storyboard and animation objects programmatically or update properties of an existing Storyboard defined in XAML.  In this case a Border control will be animated:

<Border x:Name="LightBoxControl" BorderBrush="Black" BorderThickness="3"
        CornerRadius="10"
        Height="20" Width="20" Opacity="0" Margin="5"
        MouseLeftButtonDown="Image_MouseLeftButtonDown">
    <Border.Background>
        <LinearGradientBrush EndPoint="0.893,0.116" StartPoint="0.403,0.694">
            <GradientStop Color="#FFB9B9B9" Offset="1"/>
            <GradientStop Color="#FF4F4F4F" Offset="0"/>
            <GradientStop Color="#FF666666" Offset="0.496"/>
        </LinearGradientBrush>
    </Border.Background>
    <StackPanel x:Name="spImage">
        <Border HorizontalAlignment="Right" VerticalAlignment="Top" BorderBrush="Black"
                BorderThickness="1" CornerRadius="8" Background="LightGray" Width="60" Height="25">
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12"
                       Text="Close" />
        </Border>
        <Rectangle x:Name="LightBoxImage" HorizontalAlignment="Center"
                   Fill="Black" RadiusX="10" RadiusY="10"
                   Stroke="LightGray" StrokeThickness="2" Margin="5">
        </Rectangle>
    </StackPanel>
</Border>

Here’s an example of programmatically creating a Storyboard and defining the 3 animations mentioned earlier to change the Width, Height and Opacity properties of the Border control:

double? width = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetWidth") / 1.5;
double? height = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetHeight") / 1.5;
//Programmatic way to create a storyboard and animations
Storyboard sb = new Storyboard();

//Create animation for Border control's Width
DoubleAnimation wa = new DoubleAnimation();
wa.Duration = new Duration(TimeSpan.FromSeconds(0.5))
wa.From = 0;
wa.To = width;
Storyboard.SetTarget(wa, LightBoxControl);
Storyboard.SetTargetProperty(wa, new PropertyPath(Border.WidthProperty));
sb.Children.Add(wa);

//Create animation for Border control's Height
DoubleAnimation ha = new DoubleAnimation();
ha.Duration = new Duration(TimeSpan.FromSeconds(0.5));
ha.From = 0;
ha.To = height;
Storyboard.SetTarget(ha, LightBoxControl);
Storyboard.SetTargetProperty(ha, new PropertyPath(Border.HeightProperty));
sb.Children.Add(ha);

//Create animation for Border control's Opacity
DoubleAnimation oa = new DoubleAnimation();
oa.Duration = new Duration(TimeSpan.FromSeconds(0.5));
oa.From = 0;
oa.To = 1;
Storyboard.SetTarget(oa, LightBoxControl);
Storyboard.SetTargetProperty(oa, new PropertyPath(Border.OpacityProperty));
sb.Children.Add(oa);

this.spImage.Width = width.Value - 30;
this.spImage.Height = height.Value - 30;
this.LightBoxImage.Width = width.Value - 75;
this.LightBoxImage.Height = height.Value - 75;
sb.Begin();

In this example the final height and width of the target Border control are determined by grabbing the offsetWidth and offsetHeight values using the HtmlPage class.  Once those values are determined a Storyboard object is created along with the three animations all of type DoubleAnimation.  Each animation takes 1/2 second to complete and animates the Border control’s Height, Width and Opacity properties respectively. 

Looking through the code for each animation object you’ll notice that it is associated with the target object and target object property using the Storyboard.SetTarget() and Storyboard.SetTargetProperty() static methods respectively.  This may seem strange at first glance but makes sense once you understand the concept of attached properties.  Each animation object is added to the parent Storyboard object using the Children.Add() method and the Storyboard is started by calling the Begin() method.

Interacting with Storyboards and Animations Defined in XAML

The programmatic approach to creating animations works great when there’s a lot of dynamic data being fed into animation objects.  However, in this example only the Height and Width properties are being changed on the Border control.  That’s a lot of code to write to change two properties.  Rather than defining everything programmatically you can instead define the storyboard and associated animations declaratively and fill in the dynamic pieces at runtime.  Here’s an example of defining the 3 animations shown earlier in XAML:

<Storyboard x:Name="sbShow">
    <DoubleAnimation x:Name="daWidth" Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Width"
                     From="0" Duration="00:00:0.5" />
    <DoubleAnimation x:Name="daHeight" Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Height"
                     From="0" Duration="00:00:0.5" />
    <DoubleAnimation Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Opacity"
                     From="0" To="1" Duration="00:00:0.5" />
</Storyboard>

Before the storyboard starts to play the To property of the daWidth and daHeight objects can then be assigned values as shown next:

double? width = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetWidth") / 1.5;
double? height = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetHeight") / 1.5;

//Define how big the Border control should be animated to by setting the To property value

this.daWidth.To = width;
this.daHeight.To = height;
this.spImage.Width = width.Value - 30;
this.spImage.Height = height.Value - 30;
this.LightBoxImage.Width = width.Value - 75;
this.LightBoxImage.Height = height.Value - 75;
this.sbShow.Begin();

And that’s all there is to it!  Silverlight makes it easy to work with dynamic animations whether programmatically or through modifying storyboards defined in XAML.  Download a working version of the Flickr lightbox application here if you’d like to see the animations in action (as well as some others).

 image 
Interested in learning more about Silverlight 2 application development?  Visit Interface Technical Training’s website for more information about their Silverlight 2 training course or call 1-800-264-9029.  I'll be teaching the course in the Phoenix area (or onsite at customer locations).

Using Styles to Define Silverlight 2 Control Templates

Silverlight 2 allows styles to be defined to prevent duplication of attributes across controls in a XAML file. In a previous post I demonstrated how styles could be defined and used. As a review, here’s an example of defining a style named ButtonStyle that targets a Button control:

<Style x:Key="ButtonStyle" TargetType="Button">
    <Setter Property="FontFamily" Value="Arial" />
    <Setter Property="FontWeight" Value="Bold" />
    <Setter Property="Width" Value="100" />
    <Setter Property="Height" Value="25" />
    <Setter Property="Margin" Value="5,10,0,10" />
</Style>

The style can be applied to one or more buttons using the following StaticResource syntax:

<Button x:Name="btnSubmit" Style="{StaticResource ButtonStyle}" />
<Button x:Name="btnCancel" Style="{StaticResource ButtonStyle}" />

In addition to defining control property values, styles can also be used to define control templates. This is useful any time you’d like to customize a control and make it look and feel different than it looks out of the box. In this post you’ll see how a standard rectangular Button control can be made into a rounded button. Here’s an example of what the standard Silverlight 2 Button control looks like:

image

By using control templates you can do virtually anything you’d like to a control. Here’s an example of a Button that has a custom style and control template defined to give it a flat look with rounded corners:

image

Control templates can be defined within a control's Resources section, within a UserControl's Resources section, or within the Application.Resources section of App.xaml. Defining a template in App.xaml is recommended anytime you'd like to re-use a template across multiple controls in an application.  Here's an example of a style named ButtonFlatStyle that defines a custom control template for a Button:

<Style x:Key="ButtonFlatStyle" TargetType="Button">
    <Setter Property="Width" Value="100" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
                        Background="{TemplateBinding Background}"
                        CornerRadius="8" BorderBrush="Black" BorderThickness="1">
                     <ContentPresenter Content="{TemplateBinding Content}"                          HorizontalAlignment="Center"
                         VerticalAlignment="Center"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Looking through the ButtonFlatStyle style you’ll see that it first defines a width of 100. It then defines a Template property with an associated value. Although this is a very simple example of a control template, you can see that a template is nothing more than a way to define custom control rendering. More complex templates may use many more controls, styles, bindings and even include animations using Silverlight’s Visual State Manager (a topic that I’ll save for a later article).

Control templates are defined using the ControlTemplate element which acts as a container for template controls and defines the target control type of the template by using the TargetType attribute. This example targets a Button control. The ControlTemplate element shown here includes a Border child element which is used to generate rounded corners using the CornerRadius property.

Notice that a special TemplateBinding syntax appears within several of the Border element’s attribute values. What’s a TemplateBinding and why would you use it? It’s nothing more than a way to allow control properties defined by a developer to be used in the control template. In other words, if a developer defines a Button control that uses the ButtonFlatStyle style, any Width, Height or Background values that they set on the control will automatically be used within the template. This allows the control template to take developer customizations into account.

Within the Border control you’ll see a ContentPresenter XAML element. This element marks where the control’s content (such as a Button’s Content attribute value) will be placed within the template. It literally handles “presenting” the content within the template. The Content=”{TemplateBinding Content}” attribute binds the target control’s Content property to the ContentPresenter’s Content property. To clarify this more, consider the following XAML code:

<Button x:Name="btnClear" Content="Clear"
  Background="LightGray" Style="{StaticResource ButtonFlatStyle}" />

The ContentPresenter element defined within the template will render the text “Clear” within the control template’s Border element. If a control’s Content property defines more complex content it would also be applied using the ContentPresenter. Here’s an example of defining complex content within a Button control:

<Button x:Name="btnClear2" Click="btnSubmit_Click" Background="LightGray"
  Style="{StaticResource ButtonFlatStyle}">
    <Button.Content>
        <StackPanel Orientation="Horizontal">
            <Image Source="/red_x_mark.jpg" Width="20" Height="19"
             VerticalAlignment="Center" />
            <TextBlock Margin="5" Text="Clear" Foreground="Navy"
             VerticalAlignment="Center" />
        </StackPanel>
    </Button.Content>
</Button>

In this example the content consists of additional controls rather than only basic text. Here’s an example of how the custom content would be rendered by the control template:

image

Control templates allow you to take over the look and feel of a control and customize how it’s rendered in a Silverlight 2 application. By defining control templates within styles you can easily re-use a template across multiple controls.

Silverlight Toolkit Overview and Samples

Controls are where it’s at these days in the programming world. By using them you maximize re-use, enhance productivity and avoid building custom functionality. Plus, when you need a control to do something different you can always extend it rather than writing everything from scratch.

If you’ve used ASP.NET AJAX then you’ve likely used controls from toolkits such as the ASP.NET AJAX Control Toolkit in your applications. Microsoft has also released a Silverlight Toolkit with new controls and functionality that can be used in Silverlight 2 applications. The controls are grouped into “quality bands” with the existing controls currently fitting into either the “Preview” or “Stable” bands. Additional information about quality bands and the toolkit controls can be found on Shawn Burke’s blog.

Controls included in the Silverlight Toolkit include:

  • AutoCompleteBox
  • ButtonSpinner
  • Chart
  • DockPanel
  • Expander
  • HeaderedItemControl
  • HeaderedContentControl
  • ImplicitStyleManager
  • Label
  • NumericUpDown
  • TreeView
  • ViewBox
  • WrapPanel

The new controls live in the Microsoft.Windows.Controls.dll assembly which contains several namespaces such as Microsoft.Windows.Controls (where most of the controls live) and Microsoft.Windows.Controls.DataVisualization.Charting (where the charting control and related classes live). To get started using them you’ll need to reference the Microsoft.Windows.Controls assembly provided in the toolkit and then add the controls to your Visual Studio 2008 Toolbox by right-clicking on it and selecting “Choose Items”. Select the Silverlight Components tab, browse to the assembly and then check the checkbox next to the controls you’d like to add and then drag them into a Silverlight 2 XAML file. This will add the proper namespace onto the UserControl root element as shown next:



In this post I’ll discuss a few of the new controls that I personally find quite useful and show what they’re are capable of doing. Future posts will drill into different controls and demonstrate additional details about how they can be used.

Managing Silverlight Styles with ImplicitStyleManager

In a previous post I wrote about how styles can be defined and assigned to controls. While the default technique of defining and applying styles works, it certainly can be a pain to add Style=”{StaticResource YourStyleKey}” to all of the controls needing to pick-up a specific style in an application. WPF provides a way to “implicitly” apply styles to controls but this functionality isn’t available in Silverlight 2 unfortunately. Enter the ImplicitStyleManager provided by the Silverlight Toolkit. By using ImplicitStyleManager styles that target a specific control type can be applied without manually adding a Style attribute to each control. The class lives in the Microsoft.Windows.Controls.Theming namespace (Microsoft.Windows.Controls assembly) which needs to be referenced in the XAML file as shown next:

xmlns:theming="clr-namespace:Microsoft.Windows.Controls.Theming;assembly=Microsoft.Windows.Controls.Theming"
>

Here’s an example of using ImplicitStyleManager within a control that has styles defined locally within its Resources section:


    
        
            
            
        
        
            

This example applies the styles to the appropriate controls (a Button and a TextBox in this case) automatically. The theming:ImplicitStyleManager.ApplyMode attribute makes this possible. Looking at the control definitions in the StackPanel you’ll see that no Style attribute is added. Instead, the styles are “implicitly” applied based on the Style element’s TargetType. You may also note that no x:Key is defined on the Style elements since it’s not needed.

ImplicitStyleManager can also be used to apply styles defined in a theme file (a XAML file containing a ResourceDictionary section) as shown next:


    
        

A portion of the CustomTheme.xaml file referenced by the code above is shown next:



    

    

        

            

        

        

            

        

        

            

        

        

            

        
    

Figure 2 shows how the Canvas objects and associated child objects are rendered at runtime.

Figure2

Figure 2


The StackPanel Control

The Canvas control can be used when child objects need to be absolutely positioned. While absolute positioning may be useful in some Silverlight 2 applications, others can benefit from having controls that are dynamically laid out based upon the size of the Silverlight interface. By using dynamic control layout you allow users to switch to full-screen mode without writing a lot of code to adjust control coordinates. Silverlight 2 provides the StackPanel control that can be used to stack objects horizontally or vertically and provide more flexible layouts. By using the StackPanel you can arrange controls on an interface without defining absolute positions for each control.

The StackPanel control acts much like the ASP.NET DataList control. Data can be displayed horizontally or vertically by assigning a value to the DataList control’s RepeatDirection property. RepeatDirection accepts one of two enumeration values including Horizontal and Vertical. The StackPanel control provides an Orientation property that is used to determine how to layout controls on an interface (the default layout is vertical). Like the DataList control’s RepeatDirection property, the Orientation property also accepts Horizontal and Vertical values

Although StackPanel and DataList are similar in some ways, they differ in the way data is wrapped when child controls exceed the size of the parent container. The DataList control provides a RepeatColumns property that determines how many columns are displayed before creating a new row. The StackPanel doesn’t provide that functionality unfortunately. As a result, child controls that exceed the bounds of the StackPanel will not display properly on the user interface.

WPF provides a WrapPanel control that handles wrapping child controls when they exceed the size of the parent container. Silverlight doesn’t provide a built-in WrapPanel control, however, several WrapPanel controls have been created by members of the Silverlight community that can be found using your favorite search engine.

The code that follows shows how to arrange two TextBlock controls horizontally by setting the StackPanel control’s Orientation property to Horizontal.



    

    


Controls can also be arranged vertically by changing the Orientation property to a value of Vertical:



    

    


Figure 3 shows the result of the horizontal orientation while Figure 4 shows the result of the vertical orientation. Both figures demonstrate how a StackPanel control automatically fills the width of its parent container. This default behavior can be changed by assigning a value to the control’s Width property.

Figure3

Figure 3

Figure4

Figure 4

The Grid Control

Web designers have been accustomed to arranging data and controls in tables for years. Although there’s been a general shift to CSS and div tags for page layout, table tags are still quite popular. If you’re coming from a Web development background you’ll find Silverlight’s Grid control easy to use and quick to comprehend since it’s similar to what you’ve already been using. It acts much like HTML’s table tag and allows data and controls to be arranged in a tabular-style view.

The Grid control allows rows and columns to be defined much more concisely compared to HTML. With the HTML table tag you’re forced to repeat multiple tr and td tags to create rows and columns. The Grid control allows rows and column information to be defined in one location using RowDefinition and ColumnDefinition tags. An example of XAML code that creates a simple Grid with 2 rows and 2 columns is shown next:


    
        
        
    |

    
        
        
    

This example sets the Grid’s ShowGridLines attribute to True which is nice for seeing the initial layout of a Grid control. Figure 5 shows how the Grid looks in Visual Studio 2008 when ShowGridLines is set to True.

Figure5

Figure 5

The XAML code also sets the first row’s height to 100 pixels and the second row’s height to the remaining space available in the user interface When you use the * character to define a row height you’re essentially telling the Grid to assign the row 100% of the remaining space. You could also omit the Height attribute entirely in this case and achieve the same affect. In addition to the row definitions, the two columns defined have widths of 25% and 75% respectively.

It’s important to note that the ColumnDefinition tag’s Width attribute and RowDefinition tag’s Height attribute do not accept the typical values assigned to HTML tr and td tags. For example, assigning the Width attribute a value of 25% will result in an error. If you’ve spent a lot of time creating Web pages you may struggle with this initially (I know I did!) since using the % character is so common in HTML and CSS. In Silverlight, percentage based widths are assigned by defining a decimal value between 0 and 1 followed by the * character. However, you can also assign whole numbers such as 1* and 9* for 10% and 90% respectively. In addition to numeric values, the Height and Width attributes also accept a value of Auto which causes the appropriate row or column to automatically figure out its size based on available space.

Once rows and columns are defined, controls can be placed inside of a Grid using Grid.Row and Grid.Column attributes. The following XAML shows how 4 TextBlock controls can be defined and assigned to different rows and columns of a Grid:


    
        
        
    

    
        
        
    

    
    
    
    

Figure 6 shows what the Grid control and associated child controls look like at runtime:

Figure6

Figure 6

In situations where you need a control to span multiple rows or columns you can use the Grid.RowSpan or Grid.ColumnSpan attributes. For example, the following button would be placed in the first row and span two columns:


In summary, Silverlight 2 offers several different controls that can be used to layout data and child controls on a user interface. The Canvas control allows for exact positioning whereas the StackPanel control provides more flexible control layout. The Grid control provides a simple way to arrange controls in a tabular-style on a user interface. In cases where you need additional layout flexibility such as arranging controls vertically or horizontally within a Grid cell, you can also combine layout controls such as the StackPanel (covered in a previous article) or Canvas with a Grid.

Angled Column Headers with Silverlight 2′s DataGrid

At the software shop I work at screen real-estate is always at a premium.bread and butter of our business is data, and the more of it we can fit on a screen the happier our customers are. Sometimes, to cram more data into our grids, we end up abbreviating column headers, wrapping them or possibly combining 2 or 3 data elements into a single cell. Usually this works out O.K., but sometimes we still run out of room. When this happens we either remove columns, or break the grid across separate pages.

Honestly, usually none of this is a huge problem.there is that certain class of data that just doesn’t fit well into an HTML TABLE – when the length of the data elements are substantially smaller than the data label’s.grid ends up looking too sparse.

I hadn’t thought of this before, but football statistics fit into this category pretty well (I noticed this while leafing through Sports Illustrated Fantasy Football Preview).football season is short (only 16 games) and the per game statistics that are worth counting have relatively small values (i.e, carries, receptions, touchdowns, etc…). Sports Illustrated angled the column headers to keep the grid from running off the page and I thought it looked pretty good.

So where is this going? Well, I haven’t had a chance to play with the new Silverlight 2 Beta 2 bits so I thought it might be interesting see what it would take to override Silverlight’s DataGrid to render the column headers at a 45 degree angles. I should warn you that I am *very* new to Silverlight, but I thought this was interesting enough to write up a quick post about.

Live Demo | Download

image

Binding Data to the Grid

I bound the DataGrid to a collection of WideReciever objects (anyone else excited about football being just around the corner?). It all feels a lot like working with ASP.NET‘s GridView. I just want the grid to render text, so I have bound the properties of the WideReciever objects to the DataGridTextColumn. Then I set the column header text using the Header property and then used the ElementStyle attribute to set the padding on the text that is rendered.

Here is the XAML for the DataGrid.


    
		
			
				
					
						
						
						
					
				
			
		
		
		
		
		
		
		
		
	

And here is the style setting I am using to set the padding and alignment of the cell text.

Determining the DataItemIndex

I didn’t find a way to bind the current rows index to the grid. So to work around this I wired the Loaded event of a TextBlock to run a bit of code that looks up the bound WideReciever index and populates the TextBlock with this value.

private void TextBlock_Loaded(object sender, RoutedEventArgs e)
{
	// get a reference to the TextBlock
    TextBlock textBlock = (TextBlock)sender;

	// get the data item index
	index = ((IList)this.grid.ItemsSource).IndexOf((WideReciever)textBlock.DataContext);

	// set the index
	textBlock.Text = index + 1 < 10 ? string.Format("{0}.  ", index + 1) : string.Format("{0}.", index + 1);
}

Styling the Column Headers

Finally, to get the column headers to render at an angle, I overrode the Template for the DataGrid's DataGridColumnHeaders and used a plain old Canvas to position the header text how I wanted it. To get the text to rotate I used the RotateTransform and set the angle property to -35.

That's it. Enjoy!

Pushing Data to a Silverlight Client with a WCF Duplex Service – Part II

In Part 1 of this series on pushing data to a Silverlight client with a WCF polling duplex service I demonstrated how service contracts and operations can be defined on the server.  WCF has built-in support for duplex communication (two-way communication between a service and a client) but does require a reference to System.ServiceModel.PollingDuplex.dll to make it work with Silverlight.  This assembly is provided in the Silverlight SDK and is currently in “evaluation” mode (the Silverlight go-live license doesn’t apply to it).  With the polling duplex model the Silverlight client does poll the service to check if any messages are queued so it’s not as “pure” as the sockets option available in Silverlight when it comes to pushing data from a server to a client.  However, it offers much greater flexibility when compared to sockets since it isn’t limited to a specific port range and works over HTTP.

Let’s take a look at how a Silverlight client can send and receive messages from a polling duplex WCF service and what types of messages are sent between the two.

Understanding Polling Duplex Messages

A polling duplex service communicates with a Silverlight client using WCF Message types.  This provides complete control over the data sent between the client and the service and allows communication between the two to be loosely coupled.  The downside of this is that messages must be manually serialized/deserialized by the client and service since the WSDL type information uses the xs:any element.  Here’s what the service’s WSDL types section looks like (notice the inclusion of the xs:any element) when a service uses the Message type as a parameter for an operation:



  
    
  

An example of using the WCF Message type in a WCF service is shown next.  Details about this code were covered in Part I of this series.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

namespace WCFPushService
{
    public class GameStreamService : IGameStreamService
    {
        IGameStreamClient _Client;
        Game _Game = null;
        Timer _Timer = null;
        Random _Random = new Random();

        public GameStreamService()
        {
            _Game = new Game();
        }

        public void GetGameData(Message receivedMessage)
        {

            //Get client callback channel
            _Client = OperationContext.Current.GetCallbackChannel<IGameStreamClient>();

            SendData(_Game.GetTeamData());
            //Start timer which when fired sends updated score information to client
            _Timer = new Timer(new TimerCallback(_Timer_Elapsed), null, 5000, Timeout.Infinite);
        }

        private void _Timer_Elapsed(object data)
        {
            SendData(_Game.GetScoreData());
            int interval = _Random.Next(3000, 7000);
            _Timer.Change(interval, Timeout.Infinite);
        }

        private void SendData(object data)
        {
            Message gameDataMsg = Message.CreateMessage(
                MessageVersion.Soap11,
                "Silverlight/IGameStreamService/ReceiveGameData", data);

            //Send data to the client
            _Client.ReceiveGameData(gameDataMsg);
        }
    }
}

Creating a Silverlight Duplex Polling Receiver Class

Calling and receiving data in Silverlight requires a fair amount of code to be written.  Before showing the code to interact with a polling duplex service it’s important to understand the general steps involved.  Here’s what you need to do to send and receive data in a Silverlight client:

Reference Assemblies and Namespaces

  1. Reference System.ServiceModel.dll and System.ServiceModel.PollingDuplex.dll in your Silverlight project.  Additional details on where to find the System.ServiceModel.PollingDuplex.dll assembly used by Silverlight can be found here.
  2. Import the System.ServiceModel and System.ServiceModel.Channels namespaces.

Create a Factory Object

  1. Create a PollingDuplexHttpBinding object instance and set the PollTimeout and InactivityTimeout properties (both were discussed in Part 1).
  2. Use the PollingDuplexHttpBinding object to build a channel factory.
  3. Open the channel factory and define an asynchronous callback method that is called when the open completes.

Create a Channel Object

  1. Use the factory class to create a channel that points to the service’s HTTP endpoint.
  2. Open the channel and define an asynchronous callback method that is called when the open completes.
  3. Define a callback method that is called when the channel closes.

Send/Receive Messages

  1. Create a Message object and send it asynchronously to the service using the channel object.  Define an asynchronous callback method that is called when the send completes.
  2. Start a message receive loop to listen for messages “pushed” from the service and define a callback method that is called when a message is received.
  3. Process data pushed by the server and dispatch it to the Silverlight user interface for display.

Now that you’ve seen the fundamental steps, let’s take a look at the code that makes this process work.  The following code shows a class named PushDataReceiver that encapsulates the factory and channel classes and handles all of the asynchronous operations that occur.  The class allows an object of type IProcessor to be passed into it along with a service URL, service action and initial data to send to the service (if any).  The IProcessor object represents the actual Silverlight Page class used to update data on the user interface in this case.  As data is received the Page class’s ProcessData() method will be called.

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.IO;
using System.Xml.Serialization;

namespace SilverlightPushClient
{
    public interface IProcessor
    {
        void ProcessData(object receivedData);
    }

    public class PushDataReceiver
    {
        SynchronizationContext _UiThread = null;
        public IProcessor Client { get; set; }
        public string ServiceUrl { get; set; }
        public string Action { get; set; }
        public string ActionData { get; set; }

        public PushDataReceiver(IProcessor client, string url, string action, string actionData)
        {
            Client = client;
            ServiceUrl = url;
            Action = action;
            ActionData = actionData;
            _UiThread = SynchronizationContext.Current;
        }

        public void Start()
        {
            // Instantiate the binding and set the time-outs
            PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
            {
                PollTimeout = TimeSpan.FromSeconds(10),
                InactivityTimeout = TimeSpan.FromMinutes(1)
            };

            // Instantiate and open channel factory from binding
            IChannelFactory<IDuplexSessionChannel> factory =
                binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());

            IAsyncResult factoryOpenResult =
                factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);
            if (factoryOpenResult.CompletedSynchronously)
            {
                CompleteOpenFactory(factoryOpenResult);
            }
        }

        void OnOpenCompleteFactory(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenFactory(result);
        }

        void CompleteOpenFactory(IAsyncResult result)
        {
            IChannelFactory<IDuplexSessionChannel> factory =
                (IChannelFactory<IDuplexSessionChannel>)result.AsyncState;

            factory.EndOpen(result);

            // The factory is now open. Create and open a channel from the channel factory.
            IDuplexSessionChannel channel =
                factory.CreateChannel(new EndpointAddress(ServiceUrl));

            IAsyncResult channelOpenResult =
                channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);
            if (channelOpenResult.CompletedSynchronously)
            {
                CompleteOpenChannel(channelOpenResult);
            }
        }

        void OnOpenCompleteChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenChannel(result);
        }

        void CompleteOpenChannel(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            channel.EndOpen(result);

            // Channel is now open. Send message
            Message message =
                Message.CreateMessage(channel.GetProperty<MessageVersion>(),
                 Action , ActionData);
            IAsyncResult resultChannel =
                channel.BeginSend(message, new AsyncCallback(OnSend), channel);
            if (resultChannel.CompletedSynchronously)
            {
                CompleteOnSend(resultChannel);
            }

            //Start listening for callbacks from the service
            ReceiveLoop(channel);
        }

        void OnSend(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOnSend(result);
        }

        void CompleteOnSend(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndSend(result);
        }

        void ReceiveLoop(IDuplexSessionChannel channel)
        {
            // Start listening for callbacks.
            IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);
            if (result.CompletedSynchronously) CompleteReceive(result);
        }

        void OnReceiveComplete(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteReceive(result);
        }

        void CompleteReceive(IAsyncResult result)
        {
            //A callback was received so process data
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            try
            {
                Message receivedMessage = channel.EndReceive(result);

                // Show the service response in the UI.
                if (receivedMessage != null)
                {
                    string text = receivedMessage.GetBody<string>();
                    _UiThread.Post(Client.ProcessData, text);
                }

                ReceiveLoop(channel);
            }
            catch (CommunicationObjectFaultedException exp)
            {
                _UiThread.Post(delegate(object msg) { System.Windows.Browser.HtmlPage.Window.Alert(msg.ToString()); }, exp.Message);
            }
        }

        void OnCloseChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteCloseChannel(result);
        }

        void CompleteCloseChannel(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndClose(result);
        }
    }
}

When the PushDataReceiver class’s Start() method is called by Silverlight it creates a channel factory instance which is used to create a channel instance.  The CompleteOpenChannel() callback method shown previously then sends an initial message to the service endpoint and encapsulates the data to be sent in a WCF Message object.  The message data is then sent along with the proper service action to call on the server.  After the initial message is sent a receive loop is started (see the ReceiveLoop() method) which listens for any messages sent from the server to the client and processes them accordingly.  Once a message is received the CompleteReceive() method is called and the message data is routed back to the Silverlight Page class.

Processing Data Using the XmlSerializer Class

The PushDataReceiver class shown earlier dispatches data received from the server back to the Silverlight Page class for processing.  Data sent from the server is in XML format and multiple techniques can be used to process it in Silverlight ranging from the XmlReader class to LINQ to XML functionality to the XmlSerializer class.  I chose to use the XmlSerializer class to process the data since it provides a simple way to map XML data to CLR types with a minimal amount of code.  Although you can create the CLR classes that XML data maps to by hand, I chose to create an XSD schema and use .NET’s xsd.exe tool to generate code from the schema for me.  The xsd.exe tool provides a simple way to generate C# or VB.NET code and ensures that the XML data will be successfully mapped to the appropriate CLR type’s properties.  An example of using the tool is shown next:

xsd.exe /c /namespace:SomeNamespace Teams.xsd

The /c switch tells the tool to generate classes (as opposed to strongly-typed DataSets) while the /namespace switch allows you to control what namespace is added into the auto-generated code.  Other switches are available which you can read more about here.

One of the XSD schemas used to generate C# code with xsd.exe is shown next:



  
    
      
        
          
            
              
                
                  
                  
                
              
            
            
          
        
      
    
  

Note: If you use the xsd.exe tool to generate classes that will be used in a Silverlight client you’ll have to remove a few lines that don’t compile from the auto-generated code.  The xsd.exe tool generates code designed to run on the full version of the .NET framework but with a few minor modifications you can also use the code with Silverlight.  Simply remove the namespaces and attributes that the compiler says are invalid from the auto-generated code.

Once data is received by the Silverlight client from the WCF polling duplex service it’s processed by a method named ProcessData() (the method called by the PushDataReceiver class) in the sample application.  ProcessData() uses the XmlSerializer class to deserialize XML data into custom Teams and ScoreData objects (the Teams and ScoreData classes were generated from XSD schemas using the xsd.exe tool mentioned earlier).

public void ProcessData(object receivedData)
{
    StringReader sr = null;
    try
    {
        string data = (string)receivedData;
        sr = new StringReader(data);
        //Get initial team data
        if (_Teams == null && data.Contains("Teams"))
        {
            XmlSerializer xs = new XmlSerializer(typeof(Teams));
            _Teams = (Teams)xs.Deserialize(sr);
            UpdateBoard();
        }

        //Get updated score data
        if (data.Contains("ScoreData"))
        {
            XmlSerializer xs = new XmlSerializer(typeof(ScoreData));
            ScoreData scoreData = (ScoreData)xs.Deserialize(sr);
            //ScoreDataHandler handler = new ScoreDataHandler(UpdateScoreData);
            //this.Dispatcher.BeginInvoke(handler, new object[] { scoreData });
            UpdateScoreData(scoreData);
        }
    }
    catch { }
    finally
    {
        if (sr != null) sr.Close();
    }
}

As team and score data is pushed from the server to the client it’s updated on the Silverlight interface as shown next:

The complete code for the application including the WCF duplex polling service and the Silverlight client can be downloaded here.

Twitter

Already on Twitter and interested in getting live updates about blog posts and other information?  Subscribe to my Twitter feed at www.twitter.com/DanWahlin.

Pushing Data to a Silverlight Client with a WCF Duplex Service – Part I

Silverlight provides several different ways to access data stored in remote locations.  Data can be pulled from Web Services and RESTful services and even pushed from servers down to clients using sockets (see my previous articles on sockets here, here and here).  Silverlight 2 Beta 2 introduces another way to push data from a server to a client using Windows Communication Foundation (WCF) and HTTP.  WCF’s support for duplex service contracts makes this possible and opens up unique opportunities for pumping data to Silverlight clients.  In this first part of a two part series I’ll demonstrate how a WCF push service can be created and cover the steps to get a sample service up and running.  The second article will focus on the client and show how to communicate with a WCF duplex service and listen for data that’s sent.

Many of the WCF services out there follow the simple request-response mechanism to exchange data which works well for many applications.  However, in addition to standard HTTP bindings, WCF also supports several others including a polling duplex binding made specifically for Silverlight which allows a service to push data down to a client as the data changes.  This type of binding isn’t as “pure” as the push model available with sockets since the Silverlight client does poll the server to check for any queued messages, but it provides an efficient way to push data to a client without being restricted to a specific port range.  Once a communication channel is opened messages can be sent in either direction.  The Silverlight SDK states the following about how communication works between a Silverlight client and a duplex service:

“The Silverlight client periodically polls the service on the network layer, and checks for any new messages that the service wants to send on the callback channel. The service queues all messages sent on the client callback channel and delivers them to the client when the client polls the service.”

Creating Contracts

When creating a WCF duplex service for Silverlight, the server creates a standard interface with operations.  However, because the server must communicate with the client it also defines a client callback interface.  An example of defining a server interface named IGameStreamService that includes a single service operation is shown next:
[cc lang="asp"][ServiceContract(Namespace = "Silverlight", CallbackContract = typeof(IGameStreamClient))]
public interface IGameStreamService
{
[OperationContract(IsOneWay = true)]
void GetGameData(Message receivedMessage);
}[/cc]
This interface is a little different from the standard WCF interfaces you may have seen or created.  First, it includes a CallbackContract property that points to the client interface.  Second, the GetGameData() operation is defined as a one way operation.  Client calls are not immediately returned as a result of setting IsOneWay to true and are pushed to the client instead.  The IGameStreamClient interface assigned to the CallbackContract is shown next.  It allows a message to be sent back to the client by calling the ReceiveGameData() method.
[cc lang="asp"][ServiceContract]
public interface IGameStreamClient
{
[OperationContract(IsOneWay = true)]
void ReceiveGameData(Message returnMessage);
}[/cc]

Creating the Service

Once the server and client contracts are defined a service class can be created that implements the IGameStreamService interface.  The following code creates a service that simulates a basketball game (similar to the one I demonstrated for using Sockets with Silverlight) and sends game updates to a Silverlight client on a timed basis.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

namespace WCFPushService
{
    public class GameStreamService : IGameStreamService
    {
        IGameStreamClient _Client;
        Game _Game = null;
        Timer _Timer = null;
        Random _Random = new Random();

        public GameStreamService()
        {
            _Game = new Game();
        }

        public void GetGameData(Message receivedMessage)
        {

            //Get client callback channel
            _Client = OperationContext.Current.GetCallbackChannel<IGameStreamClient>();

            SendData(_Game.GetTeamData());
            //Start timer which when fired sends updated score information to client
            _Timer = new Timer(new TimerCallback(_Timer_Elapsed), null, 5000, Timeout.Infinite);
        }

        private void _Timer_Elapsed(object data)
        {
            SendData(_Game.GetScoreData());
            int interval = _Random.Next(3000, 7000);
            _Timer.Change(interval, Timeout.Infinite);
        }

        private void SendData(object data)
        {
            Message gameDataMsg = Message.CreateMessage(
                MessageVersion.Soap11,
                "Silverlight/IGameStreamService/ReceiveGameData", data);

            //Send data to the client
            _Client.ReceiveGameData(gameDataMsg);
        }
    }
}

The service first creates an instance of a Game class in the constructor which handles simulating a basketball game and creating new data that can be sent to the client.  Once the client calls the service’s GetGameData() operation (a one-way operation), access to the client’s callback interface is retrieved by going through the OperationContext object and calling the GetCallbackChannel() method.  The teams involved in the game are then created on the server and pushed to the client by calling the SendData() method.  This method calls the Game object’s GetTeamData() method.  Although not shown here (but included in the sample code), the GetTeamData() method generates an XML message and returns it as a string.  The SendData() method then creates a WCF Message object, defines that SOAP 1.1 will be used (required for this type of communication) and defines the proper action to be used to send the XML data to the client.  The client’s ReceiveGameData() operation is then called and the message is ultimately sent to the client.

Once the client receives the team data the server will start sending simulated score data on a random basis.  When the Timer object created in the initial call to GetGameData() fires the _Timer_Elapsed() method is called which gets updated score information and pushes it to the Silverlight client by calling the SendData() method.

Creating the Service Factory

Once the service class is created a service factory can be created along with a service host.  The factory is responsible for creating the appropriate host while the host defines the service endpoint.  An example of creating service factory and host classes is shown next:

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;

namespace WCFPushService
{
    public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
    {
        public override ServiceHostBase CreateServiceHost(string constructorString,
            Uri[] baseAddresses)
        {
            return new PollingDuplexServiceHost(baseAddresses);
        }
    }

    class PollingDuplexServiceHost : ServiceHost
    {
        public PollingDuplexServiceHost(params System.Uri[] addresses)
        {
            base.InitializeDescription(typeof(GameStreamService), new UriSchemeKeyedCollection(addresses));
        }

        protected override void InitializeRuntime()
        {
            // Define the binding and set time-outs
            PollingDuplexBindingElement bindingElement = new PollingDuplexBindingElement()
            {
                PollTimeout = TimeSpan.FromSeconds(10),
                InactivityTimeout = TimeSpan.FromMinutes(1)
            };

            // Add an endpoint for the given service contract
            this.AddServiceEndpoint(
                typeof(IGameStreamService),
                new CustomBinding(
                    bindingElement,
                    new TextMessageEncodingBindingElement(
                        MessageVersion.Soap11,
                        System.Text.Encoding.UTF8),
                    new HttpTransportBindingElement()),
                    "");

            base.InitializeRuntime();
        }
    }
}


This code was pulled directly from the Silverlight SDK example which provides a great starting point for creating WCF/Silverlight polling duplex services.  The service factory class (PollingDuplexServiceHostFactory) creates a new instance of the service host class (PollingDuplexServiceHost) within the CreateServiceHost() method.  The service host class then overrides the InitializeRuntime() method and creates a PollingDuplexBindingElement instance which defines the client’s polling and inactivity timeouts.  The Silverlight SDK states the following about the PollingDuplexBindingElement class’s PollTimeout and InactivityTimeout properties:

“The PollTimeout property determines the length of time (in milliseconds) that the service holds a poll from the client before returning. The InactivityTimeout property determines the length of time (in milliseconds) that can elapse without any message exchange with the client before the service closes its session.”

The PollingDuplexBindingElement class is located in an assembly named System.ServiceModel.PollingDuplex.dll which is part of the Silverlight SDK.  You’ll need to reference the assembly in your WCF project as well as the System.ServiceModel.Channels namespace to use the PollingDuplexBindingElement class.  Once the binding element is created a call is made to the host object’s AddServiceEndPoint() method which references the PollingDuplexBindingElement object and the server’s IGameStreamService interface to create a custom binding that uses HTTP under the covers for message exchange.

Once the factory and service classes are created the factory can be referenced in the service’s .svc file in the following manner:

[cc lang="html"]<%@ ServiceHost Language="C#" Factory="WCFPushService.PollingDuplexServiceHostFactory" %>[/cc]


Looking through all of the code you can see that there’s definitely some initial setup work required to get a Silverlight callable WCF duplex service created.  Since the client does have to poll the service to check for queued messages you may wonder what the benefit is over writing a manual polling Silverlight client that calls a WCF service.  Microsoft’s Scott Guthrie was kind enough to provide additional details on that subject.  Here’s what he had to say:

“The duplex support does use polling in the background to implement notifications – although the way it does it is different than manual polling. It initiates a network request, and then the request is effectively “put to sleep” waiting for the server to respond (it doesn’t come back immediately). The server then keeps the connection open but not active until it has something to send back (or the connection times out after 90 seconds – at which point the duplex client will connect again and wait). This way you are avoiding hitting the server repeatedly – but still get an immediate response when there is data to send.”

When the client polls in the background it sends the following message to the server:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <wsmc:MakeConnection xmlns:wsmc="http://docs.oasis-open.org/ws-rx/wsmc/200702">
            <wsmc:Address>
                http://docs.oasis-open.org/ws-rx/wsmc/200702/anoynmous?id=7f64eefe-9328-4168-8175-1d4b82bef9c3
            </wsmc:Address>
        </wsmc:MakeConnection>
    </s:Body>
</s:Envelope>

In the next article I’ll demonstrate how to call a WCF polling duplex service and listen for data in a Silverlight 2 application.  An example of the Silverlight interface that will be discussed is shown next:

In the meantime, you can download the Silverlight 2 Beta 2 sample application including the WCF service and Silverlight client here.

Interested in getting live updates about blog posts and other information?  Subscribe to my Twitter feed at www.twitter.com/DanWahlin.

Silverlight 2.0 Video Tutorials

Part 1: Creating “Hello World” with Silverlight 2 and VS 2008 Tutorial Video Tutorial
Part 2: Using Layout Management Tutorial Video Tutorial
Part 3: Using Networking to Retrieve Data and Populate a DataGrid Tutorial Video Tutorial
Part 4: Using Style Elements to Better Encapsulate Look and Feel Tutorial Video Tutorial
Part 5: Using the ListBox and DataBinding to Display List Data Tutorial Video Tutorial
Part 6: Using User Controls to Implement Master/Details Scenarios Tutorial Video Tutorial
Part 7: Using Templates to Customize Control Look and Feel Tutorial Video Tutorial
Part 8: Creating a Digg Desktop Version of our Application using WPF Tutorial Video Tutorial
WP Like Button Plugin by Free WordPress Templates