Microsoft Silverlight and Truveo Video Search, Part Three

Enable the Subscriptions block here!

by Kirk Evans
November 9, 2007

This is the third and final article in a series demonstrating how to use Truveo Video Search and Silverlight together. In the first article in this series, Microsoft Silverlight and Truveo Video Search, Part 1, I showed some basic constructs of Silverlight, including the createSilverlight function. That article introduced XAML, showed how to use JavaScript to interact with events from the UI, and demonstrated how easy it is to set properties of our Silverlight application using JavaScript. The second part of our series, Microsoft Silverlight and Truveo Video Search, Part 2, showed how to dynamically generate items in the UI using the createFromXaml API call. We showed some best practices for hosting multiple Silverlight controls on a single page, and showed how easy it is to display media using the simple MediaElement in our XAML. This final article will revisit the concepts from the first two articles and introduce Silverlight 1.1, which provides the ability to use .NET-managed code instead of using JavaScript.

As this solution contains server-side code, I don't yet have a location to host a running example. However, you can find the complete managed code solution on my SharePoint site that accompanies this article.

Silverlight 1.0 is a technology focused on providing best-of-class media capabilities with a scriptable programming model, and is publicly available as a released and supported technology. Realizing that there is a huge opportunity for a best-of-class development experience beyond JavaScript, Microsoft has introduced the capability to leverage managed code across browsers and platforms. Through Silverlight, you are able to run managed .NET code in the major browsers running on both the Mac and Windows operating systems. Microsoft also announced a partnership with Novell to create Project Moonlight, where Novell will provide an implementation of Silverlight that also runs on Linux operating systems.

Why Managed Code?

The term "managed code" is used because the .NET runtime manages the lifetimes of objects and performs garbage collection. This managed code programming model uses a subset of the full Common Language Runtime (CLR) that is available across the major browsers on multiple platforms. Using managed code offers several benefits. You are able to use multiple programming languages, such as C#, VB.NET, Ruby, and Python to program Silverlight applications. This managed code runs as an add-in to the browser, allowing you a richer programming model using tools like Visual Studio to develop applications in a consistent environment and familiar tool set, as you would with ASP.NET, Compact Framework, or desktop applications. Managed code is also faster than JavaScript because it is compiled and the CLR can perform optimizations underneath the hood.

Silverlight 1.1 Deployment Topology

Let's start by looking at the topology of a deployed Silverlight 1.1 application. Managed code uses a DLL to contain the intermediate language and metadata representing a .NET assembly. This DLL file is contained in a folder, ClientBin, in your web directory. The XAML assets for your application are stored within your site's structure. One assembly can hold the source for multiple XAML assets, as shown in the screen capture below.

Deployment topology of a Silverlight 1.1 application
Figure 1. Deployment topology of a Silverlight 1.1 application

The existence of a DLL in the ClientBin folder itself is not enough to wire up the pieces of a managed Silverlight 1.1 application; we need to point the XAML to the types that extend the XAML. The XAML assets are linked to the .NET assembly through introduction of a set of attributes in the XAML's Canvas element.

<Canvas
	xmlns="http://schemas.microsoft.com/client/2007"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Loaded="Page_Loaded" 
  x:Class="AOLSilverlightVideoPlayer.Logo;assembly=
  ClientBin/AOLSilverlightVideoPlayer.dll">
    <MediaElement 
        x:Name="media" 
        MediaEnded="Media_Ended" 
        AutoPlay="True" 
        Source="images/silverlightLogoLoop.wmv" 
        Height="110" 
        Width="110" />
</Canvas>

Notice the x:Class attribute in this snippet. This attribute provides the type name of the class that supports this XAML asset, and the assembly value specifies the location of the .NET assembly. Through these two values, the XAML can be wired up to the managed code behind it, and a simpler programming model emerges. Notice the attribute Loaded on the Canvas element. This attribute points to an event in our managed code. Let's look at the managed code for our Logo type.

using System;
using System.Windows.Controls;

namespace AOLSilverlightVideoPlayer
{
    public partial class Logo : Canvas
    {
        public void Page_Loaded(object o, EventArgs e)
        {
            // Required to initialize variables
            InitializeComponent();
        }

        public void Media_Ended(object sender, EventArgs e)
        {
            media.Stop();
            media.Play();
        }
    }
}

The interesting thing to note is that we can handle initialization of our Silverlight assets in the handler for the Loaded event in managed code, and we can also easily respond to events in the XAML elements themselves without jumping back and forth into JavaScript. This is demonstrated through wiring the event handler Media_Ended to the MediaEnded event of the media control. This is much simpler than the JavaScript we used in our last article to provide the appearance of a continually looping video displayed in the browser.

We cannot completely avoid JavaScript, as JavaScript is still necessary to instantiate the Silverlight control on our web page. We will need to include the Silverlight.js script file, as this is what is used to actually create the Silverlight control and expose the createObjectEx API. As shown in the previous articles, we introduce a custom createSilverlight JavaScript function that wraps the createObjectEx API, contained in a .js script file called TestPage.html.js.

// JScript source code

//contains calls to silverlight.js, example below loads Page.xaml
function createSilverlight(source, parentId, controlId, height, width)
{
	Silverlight.createObjectEx({
		source: source,
		parentElement: document.getElementById(parentId),
		id: controlId,
		properties: {
			width: width,
			height: height,
			version: "1.1",
			enableHtmlAccess: "true"
		},
		events: {}
	});
	   
}

This JavaScript is used from our HTML page to invoke the Silverlight control.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Silverlight Project Test Page</title>
    
    <script type="text/javascript" src="Silverlight.js"></script>
    <script type="text/javascript" src="TestPage.html.js"></script>
    <link href="styles/Stylesheet1.css" rel="stylesheet" type="text/css" />
</head>

<body>    
    <div class="truveoLogo">
        <img src="images/t_logo.gif" alt="Truveo Logo" />          
    </div>
    
    <div class="criteria">			
        Search: 
        <input id="query" type="text" />
        <input id="searchButton" type="button" 
	        onclick="return Button1_onclick();" value="Go" />              
    </div>

    <div id="results" class="results">	    
	<script type="text/javascript">
		createSilverlight('scene.xaml','results','ag1','300','100%');
	</script>
    </div> 

    <div id="footer"  > 		    
        <script type="text/javascript">
		    createSilverlight('logo.xaml','footer','ag2','110','110');
        </script>	    
        <img src="images/logo_main_sl.gif" alt="Silverlight logo" />                                
    </div>			
</body>
</html>

In contrast to the previous articles, this is all the JavaScript that we will need for this solution. Not only will we manipulate the XAML elements through managed code, but our UI will also contain normal HTML elements (a button and a text box) with event handlers wired up in our managed code using Silverlight! The existence of the x:Class attribute in the XAML and the accompanying .NET assembly will play an important part of the rest of our solution.

Building a Silverlight Control

In our previous articles, we used JavaScript to dynamically add XAML to our control tree. This meant that we had to dynamically build a string, concatenate values into the string containing XAML markup, and finally use a Silverlight API to parse the string into the content. There is a much cleaner and self-contained way to achieve this in Silverlight 1.1 through building custom controls.

If you are familiar with ASP.NET development, the concept of building controls is somewhat simple. You provide the markup that represents the user interface for your control, and then provide code-behind instructions that react to events and manipulate the control itself. That paradigm is used in this example as well. We begin by introducing the XAML for our control, as it is quite simple.

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Width="100"
        Height="100"
        Background="White"
        >
  
  <Image x:Name="img" />
  <TextBlock x:Name="tb" TextWrapping="Wrap" Visibility="Collapsed" />
</Canvas>

When building a Silverlight control, it is not necessary to provide the location of the code-behind in the XAML. The connection between the XAML and the code-behind is handled within the code-behind itself.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;


namespace AOLSilverlightVideoPlayer
{
    public class VideoControl : Control
    {
        private FrameworkElement implementationRoot;
        private TextBlock tb;
        private Image img;        

        public VideoControl()
        {
            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream(
	        "AOLSilverlightVideoPlayer.VideoControl.xaml");
            implementationRoot = this.InitializeFromXaml(new 
	        System.IO.StreamReader(s).ReadToEnd());
            
            implementationRoot.MouseEnter += new 
	        MouseEventHandler(implementationRoot_MouseEnter);
            implementationRoot.MouseLeave += new 
	        EventHandler(implementationRoot_MouseLeave);

            tb = implementationRoot.FindName("tb") as TextBlock;
            img = implementationRoot.FindName("img") as Image;
        }

In the constructor for our control, we use the InitializeFromXaml method to read the XAML that was located as a resource within the assembly. We create a field in our type called implementationRoot so that we can gain access to the root Canvas of our control. Once we have the root element, we can provide event handlers as delegates for the control, such as MouseEnter, MouseLeave, and MouseLeftButtonDown. We also set field references to the TextBlock and Image elements in our XAML so that we can easily manipulate them later in our code, such as in the mouse event handlers.

       void implementationRoot_MouseLeave(object sender, EventArgs e)
        {
            tb.Visibility = Visibility.Collapsed;
            img.Opacity = 1.0;
        }

        void implementationRoot_MouseEnter(object sender, MouseEventArgs e)
        {
            tb.Visibility = Visibility.Visible;
            img.Opacity = 0.4;
        }

When the mouse enters the Canvas area, the TextBlock will become visible and the Image's opacity will be reduced to 40 percent. When the mouse leaves the Canvas area, the TextBlock is then hidden and the Image's opacity returned to 100 percent.

Beyond responding to mouse input, we also want to encapsulate some of the visual properties of our control and expose them as public properties on our type so that the control can be easily manipulated. Let's add the properties that the Truveo Video search will return to us, namely the Description, ThumbnailUrl, and VideoUrl values.

        public string VideoUrl { get; set; }

        public string ThumbnailUrl
        {
            get
            {
                return img.Source.ToString();
            }
            set
            {
                img.Source = new Uri(value);
            }
        }
        
        public string Text
        {
            get
            {
                return tb.Text;
            }
            set
            {
                tb.Text = value;
            }
        }

The VideoUrl property is using a new syntax introduced in C# 3.0, where the compiler will introduce a field to hold the values. This is simply a shortcut convenience so that we do not need to create a field and set the values ourselves. However, for the ThumbnailUrl and Text properties, we want to set a value within the implementation, so we explicitly define the get and set accessors. This is another example of convenience where we set the properties of the XAML elements directly from within our managed code.

Besides setting properties of the TextBlock and Image, we also want to control aspects of our containing Canvas as well. We can introduce new Top and Left properties that allows us to position the Canvas and its containing Image and TextBlock elements.

        public int Top
        {
            get
            {
                int ret = (int)GetValue(Canvas.TopProperty);
                return ret;
            }
            set
            {                
                SetValue<int>(Canvas.TopProperty, value);
            }
        }

        public int Left
        {
            get
            {
                int ret = (int)GetValue(Canvas.LeftProperty);
                return ret;
            }
            set
            {
                SetValue<int>(Canvas.LeftProperty, value);
            }
        }

Our VideoPlayerControl type derives from the type System.Windows.Controls.Control, which already defines properties for Width and Height. However, we might want to create our own implementation of Width and Height, so we can shadow these properties using the new keyword in C#.

        //Hide the existing Width property
        public virtual new double Width
        {
            get
            {                
                return implementationRoot.Width;
            }
            set
            {
                tb.Width = value;
                img.Width = value;
                implementationRoot.Width = value;
            }
        }

        //Hide the existing Height property
        public virtual new double Height
        {
            get
            {
                return implementationRoot.Height;
            }
            set
            {
                tb.Width = value;
                img.Width = value;
                implementationRoot.Height = value;
            }
        }
    }
}

That is our VideoPlayer control in its entirety. Using it within another Canvas is very straightforward. We define a new namespace prefix, my, that uses a value beginning with clr-namespace. This points to the CLR namespace that our control is defined in, and then we point to the location of the managed code assembly. It is then a simple act of using the control declaratively within the XAML markup.

<Canvas x:Name="parentCanvas"
        xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:AOLSilverlightVideoPlayer;assembly=
	    ClientBin/AOLSilverlightVideoPlayer.dll"
        Loaded="Page_Loaded" 
        x:Class="AOLSilverlightVideoPlayer.Scene;assembly=
	    ClientBin/AOLSilverlightVideoPlayer.dll"
        Width="640"
        Height="480"
        Background="White"
        >
  <my:VideoControl
      Height="100"
      Width="100"
      Top="10"
      Left="10"
      ThumbnailUrl="http://thumbnail.search.aolcdn.com/
          truveo/images/thumbnails/3D/AB/3DAB9A6C083215.jpg"
      Text="'Microsoft' Hopes 'Halo 3' Breaks Records - video">
  </my:VideoControl>
</Canvas>

Accessing Remote Data with Silverlight 1.1

In our previous articles, we were able to leverage the AJAX API for Truveo Video Search because we were using JavaScript: using JSON types is simple. In this version of our solution, we are using managed code. We can't use the JSON API without introducing more JavaScript, and I already made the bold statement that we are not using any more JavaScript in this version of the solution. We need another way to get the data using managed code.

Silverlight 1.1 is currently in the alpha stage, meaning it is a pre-release that you can download and use, but is not production-ready and its implementation will change. The current version provides a networking stack allowing you to access XML documents, but this capability is restricted to the same domain where the page was served from. This is typical in a sandboxed environment, and the XMLHTTPRequest object is sandboxed with similar constraints. The Silverlight team is investigating how to introduce cross-domain calls from managed code, but the current version does not support this scenario. However, server-side code is not constrained by this limitation, so the typical solution is to introduce a server-side bridge. To access data in a remote environment, you need to create a server-side bridge within your solution that can accept requests from the client and forwards them to the remote location.

Introducing a WCF Bridge

Windows Communication Foundation (WCF) is a part of the .NET framework that unifies distributed communication through a highly extensible framework. A new capability for WCF in .NET 3.5 is the ability to easily consume and expose Plain Old XML (POX) services through RESTful APIs. We will use this capability to create a WCF bridge that connects to the Truveo Video Search XML API.

We begin our look at WCF by creating a representation of the XML that Truveo will return. Simply by issuing a request through a query string such as http://xml.truveo.com/apiv3?appid=1x1jhj64466mi12ia&method=truveo.videos.getVideos&query=elvis, we will receive an XML response that is structured like the following:

<?xml version="1.0" encoding="utf-8" ?>

<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://xml.truveo.com" xsi:schemaLocation=
    "http://xml.truveo.com apiv3.xsd">
  <method>truveo.videos.getVideos</method>
  <query>elvis</query>
  <VideoSet>
    <totalResultsAvailable>43933</totalResultsAvailable>
    <totalResultsReturned>10</totalResultsReturned>
    <firstResultPosition>0</firstResultPosition>
    <title>Videos that match your query: elvis</title>
    <rssUrl>http://xml.truveo.com/rss?query=elvis&showAdult=1</rssUrl>
    <embedTag>
        <embed src="http://www.truveo.com/truveo_listWidget.swf?
	query=elvis&resultsnum=10" quality="high" wmode="transparent"
	type="application/x-shockwave-flash" width="600" height="150">
	</embed>
    </embedTag>
    <Video>
      <id>770053580</id>
      <title>'Rubberneckin' (Paul Oakenfold Remix)'</title>
      <videoUrl>http://xml.truveo.com/rd?
          i=769892927&a=70a7dc249f1af3e321b3e0e9402c6b65&p=1</videoUrl>
      <channel>AOL Music</channel>
      <channelUrl>http://music.aol.com</channelUrl>
      <dateFound>Fri, 07 Apr 2006 03:13:00 -0400</dateFound>
      <textRelevancy>286</textRelevancy>
      <vRank>0.230405</vRank>
      <description>Music video for the 2003 remix of Elvis Presley's 
          1970 song 'Rubberneckin'.' Features archival footage of Elvis and 
	  multicolor Elvis images resembling Andy Warhol's Pop Art portraits. 
	  Artists: Elvis Presley, Paul Oakenfold</description>
      <referrerPageUrl>http://video.aol.com/video/title/1101184</referrerPageUrl>
      <artist>Elvis Presley</artist>
      <album>Rubberneckin' (Remix)</album>
      <distributor>RCA</distributor>
      <copyright>Copyright 2003 BMG</copyright>
      <runtime>206</runtime>
      <category>Music</category>
      <tags>Elvis Presley, RCA, Dance and Electronica, Pop, Rock, Dance, 
          remix, Paul Oakenfold, Andy Warhol, Rubberneckin' (Remix), BMG Heritage, 
	  Dance and Electronica</tags>
      <country>United States</country>
      <language>English</language>
      <dateProduced>Sep 23 2003 12:00AM</dateProduced>
      <viewCount>1643</viewCount>
      <userRating>3.3333493333333</userRating>
      <userRatingCount>3</userRatingCount>
      <thumbnailUrl>
          http://thumbnail.search.aolcdn.com/truveo/images/thumbnails/
	      1B/B5/1BB58072C211FD.jpg
      </thumbnailUrl>
      <thumbnailUrlLarge>
          http://thumbnail.search.aolcdn.com/truveo/images/thumbnails/
	      1B/B5/1BB58072C211FD_Large.jpg
      </thumbnailUrlLarge>
      <videoResultEmbedTag>
          <embed src="http://xml.truveo.com/eb/i/769892927/
	      a/70a7dc249f1af3e321b3e0e9402c6b65/p/1/pmmsID/1101184" 
	      quality="high" bgcolor="#ffffff" width="424" height="360" 
	      name="dl_flvwidget" align="middle" allowScriptAccess="sameDomain"
	      type="application/x-shockwave-flash" 
	      pluginspage="http://www.macromedia.com/go/getflashplayer" 
	      FlashVars="">
      </videoResultEmbedTag>
      <pmmsID>1101184</pmmsID>
      <adultFlag>0</adultFlag>
      <videoPlayerEmbedTag><iframe src="http://beta.searchvideo.com/eb/
          i/769892927/a/58ef677afb89fc040e3dec6de7dd6c26/p/1/
	  pmmsID/1101184/aolflash/0/needpreviewpmmsid/0" width="425" 
	  height="415" frameborder="0" scrolling="no">
	  </iframe>
      </videoPlayerEmbedTag>
      <quality>excellent</quality>
      <formats>win, qt, real</formats>
      <bitrate>700 kbps</bitrate>
    </Video>

We need to create serializable classes that will map to this structure. This is simply done by creating a Response element with a member, VideoSet, that contains a generic list of Video elements.

using System;
using System.Runtime.Serialization;
using System.Collections.Generic;

namespace Services
{
    [DataContract(Namespace = "http://xml.searchvideo.com")]
    public class Response
    {        
        [DataMember(Name = "VideoSet")]
        public List<Video> VideoSet;
    }
    
    [DataContract(Namespace = "http://xml.searchvideo.com")]
    public class Video
    {
        [DataMember(Order=1)]
        public string id { get; set; }
        [DataMember(Order = 2)]
        public string title { get; set; }
        [DataMember(Order = 3)]
        public string videoUrl { get; set; }
        [DataMember(Order = 4)]
        public string thumbnailUrl { get; set; }
    }
}

WCF will take the XML that is received from Truveo's XML API and will map those elements based on the element name to the members of our classes. Where WCF does not see a match, it will discard the extra elements, leaving us only with the elements we are interested in.

WCF uses the concept of "contracts" to specify the characteristics of the interface between the caller and the implementation of a service. We first create a contract that maps the behavior of the Truveo XML API.

using System;
using System.ServiceModel;
using System.ServiceModel.Web;


namespace Services
{
    [ServiceContract]
    public interface ITruveoSearch
    {
        [WebGet]
        [OperationContract]
        Response Search(string appid, string method, string query);
    }
}

This code uses the WebGet attribute to add the capability for our service to be accessed through a RESTful API instead of using SOAP to call the service. This is a new feature of WCF in .NET 3.5. Next, we will define the contracts that form the exposed service with which our Silverlight application will communicate.

using System;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Services
{
    [ServiceContract]
    public interface IVideoSearch
    {
        [WebGet(UriTemplate="search?query={query}",
ResponseFormat=WebMessageFormat.Xml)] [OperationContract] Response Search(string query); } }

Again, we are using new capabilities found in .NET 3.5 where WCF can expose services not only using SOAP and HTTP POST verbs, but also with RESTful APIs through the WebGet attribute. This interface states that it is possible to call this service simply through an HTTP GET call, and it also specifies how to structure the URI for the resource as a template. We also specify that the resulting call will be an XML document.

We have a contract for our service, IVideoSearch, which will in turn call the Truveo service using the contract ITruveoSearch. The implementation code looks like the following:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace Services
{
    public class VideoSearch : IVideoSearch 
    {
        public Response Search(string query)
        {
            WebHttpBinding binding = new WebHttpBinding();
            EndpointAddress address = new 
	        EndpointAddress("http://xml.searchvideo.com/apiv3");
            ChannelFactory<ITruveoSearch> factory = new 
	        ChannelFactory<ITruveoSearch>(binding, address);
            factory.Endpoint.Behaviors.Add(new WebHttpBehavior());

            ITruveoSearch proxy = factory.CreateChannel();
            Response returnValue = proxy.Search("1x1jhj64466mi12ia", 
	        "truveo.videos.getVideos", query);
            return returnValue;
        }
    }
}

Notice where each contract is being consumed. The VideoSearch service is exposed publicly, and the ITruveoSearch contract is being used internally to consume the Truveo service. We also repurpose the Response data transfer object that we created previously, greatly simplifying our code so that we do not need to do XML mapping or even look at angle brackets at all in our service!

Consuming Remote XML Documents in Silverlight 1.1

The final piece of our managed code Silverlight solution is to call our WCF bridge, which simply issues an HTTP GET call that returns XML. We did not have to use WCF to create the bridge; nor did we have to use any .NET technology. The only requirement is that Silverlight can request the XML document from the same domain; WCF was a huge convenience for us in this case because it simplifies the programming model significantly.

If you recall from early on in this article, we defined an HTML UI that contained an HTML input type="button" and an input type="text" to allow the user to provide input. When the button is clicked, we want to grab the value from the text box and send its value to our WCF bridge. Wiring that up with Silverlight 1.1 is very easy.

using System;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml;
using System.Net;
using System.IO;
using System.Windows.Browser;
using System.Windows.Browser.Net;
using System.Windows.Media;

namespace AOLSilverlightVideoPlayer
{
    public partial class Scene : Canvas
    {        
        public void Page_Loaded(object o, EventArgs e)
        {
            // Required to initialize variables
            InitializeComponent();            
                                
            HtmlElement button = HtmlPage.Document.GetElementByID
("searchButton"); button.AttachEvent("onclick", new EventHandler
(SearchClicked)); }

When the button is clicked, that will fire the SearchClicked event. This event is where we actually grab the value from the text box.

        public void SearchClicked(object sender, System.EventArgs e)
        {
            Children.Clear();
            string serverUri = HtmlPage.DocumentUri.ToString();
            int thisApp = serverUri.IndexOf("/TestPage.html");
            HtmlElement textBox = HtmlPage.Document.GetElementByID("query");

            serverUri = serverUri.Substring(0, thisApp) + "
/Truveo.svc/search?query=" + textBox.GetProperty<string>("value"); BrowserHttpWebRequest request = new BrowserHttpWebRequest(new
Uri(serverUri)); // Include the request object as the IAsyncResult.AsyncState
property. IAsyncResult iar = request.BeginGetResponse(new AsyncCallback
(OnResponseComplete), request); }

We are using an asynchronous programming model in this case, so as not to block the primary UI thread. When the asynchronous call is completed, that will fire the OnResponseComplete method. This programming model should be very familiar to JavaScript developers who have worked with the XMLHTTPRequest model.

        public void OnResponseComplete(IAsyncResult iar)
        {            
            try
            {
                // Use the request object from the IAsyncResult.AsyncState 
property // to obtain the response. HttpWebResponse response = ((HttpWebRequest)iar.AsyncState).EndGetResponse(iar); if (response.StatusCode != HttpStatusCode.OK) throw new ApplicationException("HttpStatusCode " + response.StatusCode.ToString() + " was returned."); using (XmlReader xr = XmlReader.Create
(response.GetResponseStream())) { RenderControls(xr); } response.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); } }

Dynamically Adding Silverlight Controls

When we receive a response back from the HttpWebRequest, we can obtain a reference to its underlying stream and create an XmlReader from that stream. The XmlReader is a pull API that iterates in a cursor-style fashion over an XML document. Rather than loading the entire document into a DOM document, the XmlReader simply scans forward through the stream and represents parts of it with a cursor model. This yields a highly efficient way to process large XML documents. The API is very easy to use as well, as we see in our RenderControls method.

        private void RenderControls(XmlReader reader)
        {

            int i = 0;
            int top = 10;
            int left = 10;
            while (reader.ReadToFollowing("Video", "http://xml.searchvideo.com"))
            {
                VideoControl ctl = new VideoControl();

                //Move to child ID element
                reader.Read();
                string id = reader.ReadElementContentAsString();
                ctl.Text = reader.ReadElementContentAsString();
                ctl.VideoUrl = reader.ReadElementContentAsString();
                ctl.ThumbnailUrl = reader.ReadElementContentAsString();
                ctl.Top = top;
                ctl.Left = left;

                Children.Add(ctl as Visual);

                i++;
                if (i % 5 == 0)
                {
                    left = 10;
                    top += 110;
                }
                else
                {
                    left += 110;
                }
            }         
        }
    }
}

The RenderControls method brings us full circle in our solution. We are able to process the remote XML and use the values returned from our WCF bridge to create instances of our VideoPlayer control. We also use a modulus operator to determine how to create columns and rows of the data without prior knowledge of the number of results returned from the Truveo service.

That's our solution in its entirety. As this solution contains server-side code, I don't yet have a location to host a running example. However, you can find the complete managed code solution on my SharePoint site.

References

For more information on the techniques used in this article, here are a number of resources for further information.