Posts Tagged with Dispatcher.BeginInvoke

Asynchronous loading of TimelineMarkers for Silverlight Video Player

As part of the ongoing evolution of our Open Source Silverlight Video Player on CodePlex, I added the ability to asynchronously load a set of TimeLineMarkers from an XML document.  The goal was to allow users a choice between encoding there video with markers or defining those markers in an external xml file.  What I thought would be a pretty trivial task, proved to be an interesting challenge. Here is a snapshot of the Xml format.

marker_1


The first step in implementing this functionality was to download the marker Xml.  In the event that this marker file was quite large in size I decided to make the download asynchronous.  Downloading a file asynchronously required that the path to the file is absolute.  Since I have no real control over my initParams I decided to support both an absolute or relative Uri.  In the event that the path to the Xml document is relative, I make a reasonable assumption that it is located in the same location as the XAP.  As a result ,I can define my Uri with “Host.Source” as the baseUri of the Xml document.  To Download the file, I simply make a WebRequest and define an AsyncCallback to be fired when the file has completed downloading. 

Threading Considerations

In Silverlight 2 Beta 2 there was a significant change in the concurrency model used for asynchronous communications.  In Beta 1 these type of requests returned on the UI thread.  In Beta 2, when you choose to use the BeginGetResponse of the WebRequest you are telling Silverlight to use a worker thread that comes from a thread pool.  As a result, you can NOT update any user interface elements on the UI thread.  Initially I thought I would be able to use “Application.Current.RootVisual.Dispatcher” as a way to execute a delegate on the UIThread.  Unfortunately this throws a Cross Thread exception.  After explored a few options I realized that I could simply call Dispatcher.BeginInvoke(MyMethod) from within my AsyncCallback.  here is what that looked like.

void OpenStreamCompleted(IAsyncResult ar)
{
    HttpWebRequest request = ar.AsyncState as HttpWebRequest
    WebResponse response = request.EndGetResponse(ar)
 
    // Read the response into a Stream object.
    Stream responseStream = response.GetResponseStream()
 
    // Create a new StreamReader instance using the specified Stream object.
    using (StreamReader streamreader = new StreamReader(responseStream))
    {
        XElement document = XElement.Load(streamreader) 
        _externalMarkerData = (from el in document.Elements() select GetMarkerData(el)).ToList()
    }
    Dispatcher.BeginInvoke(ProcessParsedMarkerCollection)
}

As you can see above, once I have the Xml downloaded I parse it from Xml into a collection. For this I thought it would be cool to use Linq for XML.  I am always amazed at how few a lines of code I need to write to parse an Xml document with Linq.  The end product of the parse is a List<> containing a very simple custom object called “MarkerData”.  If I had chosen to move this parsing back to the UIThread I could have just created a collection of TimelineMarkers, but I thought there was no harm in having my own object to expand over time.

To get the latest source visit the project at http://www.codeplex.com/sl2videoplayer