Enhance your development experience with the ESRI Silverlight API using the Microsoft Reactive Extensions
In my humble opinion, Reactive Extensions is one of the most exciting libraries to come out of Microsoft lately. And that is saying a lot since there have been quite a bit of good libraries coming out of Redmond recently. The Rx for .NET takes a lot of pain out of asynchronous programming and has drastically reduced the amount of code required to handle and orchestrate responses from multiple web services. Given that Silverlight is a client-side technology, most of the Silverlight applications make calls to web services and display results to the user as needed. A common application scenario is where the Silverlight client needs to make multiple calls to multiple web services and orchestrate calls to other web services based on responses from previous web service calls. Handling this scenario can get quite a bit complicated when you have to maintain contexts to store response states from the various web services. This is where Rx for .NET comes in real handy. The following scenario and the solutions should illustrate how Rx for .NET can help when programming with the ESRI Silverlight API for example.
When adding ArcGIS Map service layers to the Map control in the ESRI Silverlight API, the map control makes a HTTP request for every layer added to request the metadata for the map service. Depending on whether the request to the service metadata succeeds or fails the layer object would raise a ‘Initialized’ event or a ‘InitializationFailed’. So, a layer ‘load’ can be considered to be complete when either the ‘Initialized’ or the ‘InitializationFailed’ has fired. If we want to initiate another sequence of operations when all the ‘load’ of all the layers on the map have completed, like say populate a legend/ Table of Contents, then we will have to wait for all the layer ‘load’ to be complete (Success/Failure). Making a request to the legend web service in the above scenario can get quite cumbersome. But Rx for .NET can simplify things quite a bit. Follow the code sample below to accomplish the task.
The two extension methods below will help us watch for the ‘load’ complete for a layer and to watch for all the layers in a LayerCollection to complete. As you can see, we use the ‘Merge’, ‘Take’ and the ‘ForkJoin’ observable extension methods on the IObservable to watch for the right sequence of events.
public static class ExtensionMethods
{
public static IObservable<IEvent<EventArgs>> GetLayerLoadCompleted( this Layer layer )
{
if( layer == null )
throw new ArgumentNullException( "layer" );
if( layer.IsInitialized == true )
{
IEvent<EventArgs>[] events = new IEvent<EventArgs>[ 1 ] { null };
events.ToObservable<IEvent<EventArgs>>();
}
IObservable<IEvent<EventArgs>> oSuccess = Observable.FromEvent<EventArgs>
(
ev => layer.Initialized += ev,
ev => layer.Initialized += ev
);
IObservable<IEvent<EventArgs>> oFailed = Observable.FromEvent<EventArgs>
(
ev => layer.InitializationFailed += ev,
ev => layer.InitializationFailed += ev
);
IObservable<IEvent<EventArgs>> obsCompleted = oSuccess.Merge<IEvent<EventArgs>>( oFailed );
IObservable<IEvent<EventArgs>> oFirst = obsCompleted.Take<IEvent<EventArgs>>(1);
return oFirst;
}
public static IObservable<IEvent<EventArgs>[]> GetLayersLoadCompleted( this IEnumerable<Layer> layers )
{
if( layers == null )
throw new ArgumentNullException( "layers" );
return Observable.ForkJoin<IEvent<EventArgs>>
(
from item in layers
select item.GetLayerLoadCompleted()
);
}
}
And now, we can add a handler to be executed when all the layers have loaded like below.
public MainPage()
{
InitializeComponent();
IDisposable subscription = null;
subscription = myMap.Layers.GetLayersLoadCompleted().Subscribe
(
( args ) =>
{
HtmlPage.Window.Alert( "All layers have loaded." );
subscription.Dispose();
}
);
}
4 comments