Rx has been released. Enjoy.
unfold
Tuesday, November 17, 2009
Friday, October 23, 2009
Silverlight Toolkit Drag Drop (Part 2): Customizing Drag and Drop Behavior
In part 1 you were introduced to the new Silverlight DragDropTarget controls. In addition to being powerful these controls are very flexible, allowing developers to customize almost every single aspect of their behavior. In order to understand how to customize these controls you must first understand how they communicate. The good news is that DDT’s communicate via a WPF-compatible implementation of the drag/drop events!
A WPF-Compatible (mostly) Implementation of Drag and Drop
The Silverlight Toolkit Drag and Drop Framework is a compatible subset of WPF’s drag and drop methods and events – with a few small exceptions. The diagram below demonstrates how DDTs are layered on top of this WPF-compat framework. DDTs use these events and methods to communicate with each other.
The fact that the Silverlight Toolkit contains a full implementation of WPF’s drag and drop stack gives the developer tremendous flexibility. They can…
- Use DDTs.
- Communicate with and customize DDTs by handling the drag and drop events.
- Choose not to use DDTs and use the WPF-compat API’s to initiate and manage their own drag operations.
- Add native drag and drop support to their custom controls by implementing the IAcceptDrop interface.
- Create new DDTs for existing controls by creating a class that inherits from DragDropTarget.
Differences between Silverlight Toolkit and WPF Drag and Drop API’s
Different Namespaces
The most obvious difference between the Silverlight implementation of drag and drop and that of WPF is that the Silverlight drag and drop objects are located in the Microsoft.Windows namespace rather than in the System.Windows namespace. When writing drag and drop code it’s good practice to insert the following code in your source file:
#if SILVERLIGHT using Microsoft.Windows; using SW = Microsoft.Windows; #else using SW = System.Windows; #endif
Using an alias for the namespace with the drag/drop objects ensures that you can write code that can be easily ported to WPF or a future version of Silverlight with core-level drag/drop support. All of the code samples in this series of blog posts assume that you’ve defined the alias “SW” as Microsoft.Windows in Silverlight and System.Windows in WPF.
Starting a Drag Operation
Keep in mind that if you intend to use a DDT you don’t need to know how to do this because DDT’s automatically initiate drag operations when an item is dragged. However if you want to initiate a drag operation yourself you’ll need to know about some subtle (but necessary) differences between the Silverlight’s and WPF’s DoDragDrop methods.
In WPF the DoDragDrop method blocks until the drag operation completes. In Silverlight there are no blocking UI methods so it is necessary to listen for a DragDropCompleted event if you want to perform an operation when a drag completes.
It is still possible to write cross-platform code if you use the following pattern:
// Define an action to be executed when the drag completes. Action<SW.DragDropEffects> dragDropCompleted = effects => { Console.WriteLine(string.Format(“Drag finished: {0}”, effects))); }; #if SILVERLIGHT // Create a handler for the DragDropCompleted event which invokes the action. EventHandler<DragDropCompletedEventArgs> dragDropCompletedHandler = null; dragDropCompletedHandler = (sender, args) => { // Detach the handler so that this action is only executed once. SW.DragDrop.DragDropCompleted -= dragDropCompletedHandler; dragDropCompleted(args.Effects); }; // Attach the handler to the DragDropCompleted event. SW.DragDrop.DragDropCompleted += dragDropCompletedHandler; // Begin the drag operation. SW.DragDrop.DoDragDrop( sourceControl, data, SW.DragDropEffects.All, SW.DragDropKeyStates.LeftMouseButton); #else // If WPF then just begin the drag operation. SW.DragDropEffects effects = SW.DragDrop.DoDragDrop( sourceControl, data, SW.DragDropEffects.All); // Code blocks until drag is finished... dragDropCompleted(effects); #endif
Note that in addition to being asynchronous the Silverlight version of DoDragDrop accepts an additional parameter: the initial DragDropKeyStates. In the example below this is SW.DragDropKeyStates.LeftMouseButton.
SW.DragDrop.DoDragDrop(
sourceControl,
data,
SW.DragDropEffects.All,
SW.DragDropKeyStates.LeftMouseButton);In Silverlight (unlike WPF) it is not possible to sample the mouse state. Therefore the developer must track the state of the mouse keys and pass it to DoDragDrop. This is straightforward if, as is commonly the case, the drag operation begins with a mouse click.
button.MouseLeftButtonDown += (sender, args) => { SW.DragDrop.DoDragDrop( button, "Some text to transfer", SW.DragDropEffects.All, SW.DragDropKeyStates.LeftMouseButton); };
Although the DragDropKeyStates enumeration is a flags enum which includes members that track keyboard states there is no need to include these. The DoDragDrop method will sample the keyboard and update the value when invoked.
Listening to Drag Source and Drag Target Events
In WPF there are two ways to attach a handler to an event. The first is to attach a handler to the event on the UIElement and the second is to attach a handler to the attached event object:
// Works in WPF. myButton.DragOver += new SW.DragEventHandler((o, a) => Console.Write(“button dragged over.”)); // Works in WPF and Silverlight Toolkit Drag Drop Framework. myButton.AttachEvent( SW.DragDrop.DragOverEvent, new SW.DragEventHandler((o, a) => Console.Write(“button dragged over.”), false);
Silverlight Toolkit’s Drag and Drop Framework supports the latter approach for all drag target and source events. By using AttachEvent and RemoveEvent you can write code that will work on WPF and Silverlight. For more information on attached events see the Attached Events Overview on MSDN.
Note: DDTs implement the familiar WPF events so you don’t need to use the more verbose attached event approach when you’re working with them.
// DragDropTarget's have the WPF events. myListBoxDragDropTarget.DragOver += new SW.DragEventHandler( (o, a) => Console.Write(“DDTs make everything easier!”));
Indicating that an Element Can Accept a Drop Operation
In WPF all UIElements have an AllowDrop property which determines whether they will receive drag/drop events. As it isn’t possible to add properties to existing types the Silverlight Tookit implementation of drag and drop uses an AllowDrop attached property on the static DragDrop object:
WPF
<Rectangle AllowDrop="True" />
Silverlight
<Rectangle xmlns:msWindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit" mswindows:DragDrop.AllowDrop="True" />
Customizing DragDropTarget Behavior
DDT’s do their best to determine the allowed effects of a drag operation by examining the bound collection and the items inside of them. For example if a DDT’s control is bound to a ReadOnlyCollection or an array then the allowed effects of a drag will not include DragDropEffects.Move and the allowed effects of a drop will be DragDropEffects.None. By default a DDT never sets the allowed effects of a drag to DragDropEffects.Copy because there is no generic way of cloning an item in Silverlight (ICloneable does not exist). But what if we wanted the effect of a drop to be a Copy operation?
Let’s say we have two DDT’s, one containing a ListBox and the other containing a DataGrid.
<StackPanel Orientation="Horizontal"> <controlsToolkit:ListBoxDragDropTarget x:Name="listBoxDragDropTarget" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <ListBox x:Name="listBox" VerticalAlignment="Stretch" SelectionMode="Extended" Height="300" > <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Image}" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="4,0,0,0" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </controlsToolkit:ListBoxDragDropTarget> <dataToolkit:DataGridDragDropTarget x:Name="dataGridDragDropTarget" msWindows:DragDrop.AllowDrop="true" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <data:DataGrid x:Name="dataGrid" AutoGenerateColumns="False" Height="300" > <data:DataGrid.Columns> <data:DataGridTextColumn Binding="{Binding Name}" Header="Name" /> <data:DataGridTextColumn Binding="{Binding Reputation}" Header="Reputation" /> <data:DataGridTextColumn Binding="{Binding GamerScore}" Header="GamerScore" /> </data:DataGrid.Columns> </data:DataGrid> </dataToolkit:DataGridDragDropTarget> </StackPanel>
Each control is bound to an collection of Gamer instances.
class Gamer { public string Name {get; set} public double Reputation {get; set} public int GamerScore {get;set;} // Our custom copy method. public Gamer Clone() { return new Gamer { Name = this.Name, Reputation = this.Reputation, this.GamerScore = this.GamerScore }; } } // snip... // No need to use an ObservableCollection. We don't // want items removed from the ListBox. listBox.ItemsSource = new [] { new Gamer { Name = "Mark Rideout", Reputation = 82.234, GamerScore = 23432 }, // etc. }; // Using an observable collection ensures that changes // to the items source collection will be reflected // in the grid immediately. dataGrid.ItemsSource = new ObservableCollection<Person>();
When the user drags Gamers from the ListBox to the DataGrid we’d like to add a copy of the Gamer object to the DataGrid rather than add a reference to the object in the ListBox to the DataGrid’s bound collection (the default action). In order to ensure that when an item is dragged from a ListBox the allowed effect is DragDropEffects.Copy rather than DragDropEffects.Link we set the ListBoxDragDropTarget’s AllowedSourceEffects property.
listBoxDragDropTarget.AllowedSourceEffects = SW.DragDropEffects.Copy;
Since the DataGridDragDropTarget doesn’t know how to handle a Copy operation we need to handle its Drop event.
using System.Collections.ObjectModel; // snip... dataGridDragDropTarget.Drop += (sender, args) => { // Only handle this event if it's a copy. If the event is not handled // the DDTs base implementation of Drop will execute. if ((args.AllowedEffects & SW.DragDropEffects.Copy) == SW.DragDropEffects.Copy) { IList<Person> dataSource = dataGrid.ItemsSource as IList<Person>; // Retrieve the dropped data in the first available format. object data = args.Data.GetData(args.Data.GetFormats()[0]); // The data is the ItemDragEventArgs that was created by the DDT when // the drag started. It contains a SelectionCollection. // SelectionCollection's are used by DDTs because they can transfer // multiple objects. The fact that they store the indexes of the // objects within the source collection also makes reordering items // within a source possible. ItemDragEventArgs dragEventArgs = data as ItemDragEventArgs; SelectionCollection selectionCollection = dragEventArgs.Data as SelectionCollection; if (selectionCollection != null) { IEnumerable<Gamer> gamers = selectionCollection.Select(selection => selection.Item).OfType<Gamer>(); if (gamers.Any()) { foreach(Gamer gamer in gamers) { // Invoke our custom Clone method to make a copy. dataSource.Add(gamer.Clone()); } args.Effects = SW.DragDropEffects.Copy; args.Handled = true; } } } };
Now when we drag items from our ListBox to our DataGrid we will get copies of the Gamer objects and the ListBox’s contents will be unchanged.
There are many more extensibility points in DDT’s. I encourage you to take a close look at the events and properties. I think you’ll be impressed by how flexible these controls are.
Powerful and Flexible
By now it should be clear that the Silverlight Toolkit Drag and Drop Framework is more than a few DDTs that add drag and drop behavior to a certain Silverlight controls. It is also a rich set of WPF-compatible events and methods that can be used with any Silverlight control - including your own!
Next time: Adding Drag and Drop support to your custom controls!
Monday, October 19, 2009
New with the Silverlight Toolkit: Drag and Drop Support for all your Favorite Controls! (Part 1)
One of the highest voted requests on the Silverlight Toolkit CodePlex page is drag and drop support for the TreeView control. The good news is the Toolkit team listened and has not only delivered Drag and Drop for the TreeView, but they’ve added support for all the major Silverlight controls! In the October Toolkit refresh you’ll find controls which add Drag and Drop support to all of your favorite controls – no code required!
Introducing The Drag/Drop Target Controls
A DragDropTarget is a Content Control that adds default drag and drop actions to the control nested inside of it. DragDropTarget’s provide the following functionality:
1. Initiates a drag operation when an item container is dragged.
2. Displays a snapshot of the the item container by the mouse while it is being dragged.
3. Handles drag target events and specifies which drag operations are possible by examining the item source bound to the nested control.
4. If an item is dropped onto the drag drop target, it is added to the nested control if the nested control is bound to an ObservableCollection (or any collection that implements INotifyCollectionChanged and contains the same type of items as the item that was dropped).
5. Where possible, scrolls vertically and horizontally when an item is dragged near the edge of the control.
This release of the toolkit introduces the following implementations:
- ListBoxDragDropTarget
- TreeViewDragDropTarget
- DataGridDragDropTarget
- DataPointSeriesDragDropTarget
ListBoxDragDropTarget
In this example we have a ListBox created in XAML and bound to an observable collection.
<ListBox x:Name=”myListBox />
To add drag and drop functionality to our ListBox we nest it inside of the ListBoxDragDropTarget control…
<toolkit:ListBoxDragDropTarget xmlns:toolkit="System.Windows.Controls.Toolkit" xmlns:mswindows="Microsoft.Windows" mswindows:DragDrop.AllowDrop=”True”> <ListBox x:Name="myListBox"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </toolkit:ListBoxDragDropTarget>
…and that’s it! Now we can drag items to and from our ListBox.
If the SelectionMode of the ListBox is set to Multiple or Extended it is also possible to drag and drop several items at once.
“Why did you need to specify the ItemPanelTemplate?”
The ListBoxDragDropTarget cannot reorder items in a ListBox when it is virtualized because it cannot conclusively determine the index of each item. Therefore we replace the default Panel (VirtualizedStackPanel) with a normal StackPanel. If you don’t need to enable the reordering items within the ListBox then it isn’t necessary to specify the ItemPanelTemplate.
TreeViewDragDropTarget
The TreeViewDragDropTarget is used the same way as the ListBoxDragDropTarget.
<controlsToolkit:TreeViewDragDropTarget msWindows:DragDrop.AllowDrop="true" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <controlsToolkit:TreeViewDragDropTarget.Resources> <win:HierarchicalDataTemplate x:Key="hierarchicalTemplate" ItemsSource="{Binding Reports}"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Image}" Width="16" Height="16" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="4,0,0,0" /> </StackPanel> </win:HierarchicalDataTemplate> </controlsToolkit:TreeViewDragDropTarget.Resources> <controls:TreeView ItemTemplate="{StaticResource hierarchicalTemplate}" Height="300" > </controls:TreeView> </controlsToolkit:TreeViewDragDropTarget>
Note that there is no need to specify the ItemPanelTemplate because in Silverlight the TreeView is not virtualized.
The TreeViewDragDropTarget provides all the functionality you’ve come to expect from Windows Explorer (and more).
- During a drag operation parent’s expand when hovered over after a short delay
- Dragging a parent into itself is forbidden
- The ability to drag above, below, and into a node is supported
All of these actions can be overridden or cancelled using the events.
DataGridDragDropTarget
Using the DataGridDragDropTarget is straightforward:
<dataToolkit:DataGridDragDropTarget msWindows:DragDrop.AllowDrop="true" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <data:DataGrid x:Name="dataGrid" AutoGenerateColumns="False" Height="300" > <data:DataGrid.Columns> <data:DataGridTextColumn Binding="{Binding Name}" Header="Name" /> <data:DataGridTextColumn Binding="{Binding Reputation}" Header="Reputation" /> <data:DataGridTextColumn Binding="{Binding GamerScore}" Header="GamerScore" /> </data:DataGrid.Columns> </data:DataGrid> </dataToolkit:DataGridDragDropTarget>
Like the ListBoxDragDropTarget, multiple selection is supported. However the DataGridDragDropTarget has some limitations due to the fact that it is not an ItemsControl and it is virtualized, specifically that reordering items within a DataGrid is not supported.
DataPointSeriesDragDropTarget
The DataPointSeriesDragDropTarget supports all of the series that ship with Silverlight Charts. However it is used slightly differently than other DragDropTargets. Instead of wrapping the individual series it is necessary to put the entire Chart in the DragDropTarget.
<chartingToolkit:DataPointSeriesDragDropTarget msWindows:DragDrop.AllowDrop="true" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"> <chartingToolkit:Chart x:Name="chart" Background="White"> <chartingToolkit:ColumnSeries IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding Reputation}" Title="Reputation" /> <chartingToolkit:PieSeries IndependentValueBinding="{Binding Name}" DependentValueBinding="{Binding GamerScore}" Title="Gamer Score" /> </chartingToolkit:Chart> </chartingToolkit:DataPointSeriesDragDropTarget>
“What if a Chart contains multiple series?”
The DataPointSeriesDragDropTargets allows diambiguation between the series by detecting drop operations on the legend. In other words, items can be dropped both onto the surface of a series or its legend items. In the example below the Chart contains two series: a Pie series and a column series. Items can be dragged from a list box into the column series by dropping the items on the column series’ legend item.
It’s also easy to drag items between series, either by dropping data directly onto a series data point or onto their legend item entries.
A Labour of Love
Silverlight Drag and Drop support is a personal, off-hours project that I’ve been working on sporadically for the last few months. I thought it would be a productive way of learning Rx, the new reactive programming framework for .NET. When I approached the Toolkit team and asked if they would ship it they agreed. Of course, having formerly worked for the Toolkit team creating the Silverlight Chart control, ISM, and the Rating control I had some pretty good contacts ;-).
Next time: Overriding the Default Actions of DragDropTargets
Saturday, August 1, 2009
The Joy of Rx: Building an Asynchronous API with Rx and IQueryable (Updated)
Updated: One of our architects expressed concerns about my original solution to this problem because it required a down-cast to IObservable. In the process of explaining to him why this was unavoidable I figured out that it was, in fact, avoidable. :-) I've since updated this article with a much more elegant solution.
The IObservable interface is a true Microsoft innovation and a very significant discovery. Although we've known for a long time that iteration could be done either via push or pull we hadn't figured out how to do push iteration in a composable way until IObservable came along. It's interesting to speculate about how the .NET framework might've been designed differently if IObservable had been present alongside IEnumerable in .NET 1.0. Here are a few questions that haunt me...
- Rather than introduce the redundant concept of an event into the CLR would we have used properties of type IObservable instead?
- Would all asynchronous API's return an IObservable rather than use one of any number of other patterns.
- Would the "yield" keyword in C# work for methods that return IObservable as well as those that return IEnumerable?
- Would the ItemsSource property of the Silverlight/WPF ItemsControl (which most data controls derive from or are modeled after) be of type IObservable (or object) rather than IEnumerable?
There's no way of knowing for sure but I like to think the answer to these questions is "yes." There is yet another question worth asking now with the benefit of hindsight:
If IObservable had been in BCL all along would IQueryable inherit from IObservable as well as IEnumerable (or neither)?
Probably. The most common use of IQueryable is to execute query logic on a remote machine. Because they take a relatively large amount of time it makes sense to make remote calls asynchronously and expose a non-blocking interface. Ideally IQueryable wouldn't inherit from either IObservable or IEnumerable. Instead it probably should've had its own implementation of the query operators and provided two conversion methods: ToEnumerable and ToObservable. Unfortunately this would be a breaking change at this point and is therefore highly unlikely to happen.
"Okay, okay. You're bumming me out. How can I traverse an IQueryable asynchronously?"
Before we take a look at an example it might be helpful to have a look at the first part of the excellent "Building a Linq IQueryable Provider" series on the Wayward Weblog. I'm going to use the code in that article as a starting point.
Asynchronously Querying Yahoo Finance
Yahoo Finance has a REST service that returns stock history in a CSV file. We'd like to be able to query for stocks using an API like this:
YahooFinance service = new YahooFinance(); IQueryable stockInfos = from stock in service.Stocks where stock.Name == "MSFT" where stock.Date > new DateTime(1999, 1, 1) select stock;
First we'll create a simple C# class which will wrap the REST service and expose an asynchronous API. To build it we'll use the GetDownloadString extension method we introduced to WebClient in my last Rx post.
public class YahooFinanceStockService { private WebClient client = new WebClient(); const string uriFormat = "http://ichart.finance.yahoo.com/table.csv?s={0}&a={1}b={2}&c={3}&d={4}&e={5}&f={6}&g=d&ignore=.csv"; public IObservable<StockInfo> GetStockInfo(string name, DateTime from, DateTime to) { return client.GetDownloadString( new Uri( string.Format( uriFormat, name, from.Day, from.Month, from.Year, to.Day, to.Month, to.Year))) .SelectMany( text => ( // retrieve each line in the CSV file from line in text // remove trailing line break at end of file .Trim() .Split('\n') // skip header line .Skip(1) let fields = line.Split(',') select new StockInfo { Name = name, Date = DateTime.Parse(fields[0]), Open = double.Parse(fields[1]), High = double.Parse(fields[2]), Low = double.Parse(fields[3]), Close = double.Parse(fields[4]), Volume = int.Parse(fields[5]), AdjustedClose = double.Parse(fields[6]) } ).ToObservable()); } }
Next we'll create a YahooFinanceQueryProvider that derives from the QueryProvider class introduced in the Wayward Weblog series. The query provider interprets the query expression, makes one or more calls to the service's GetStockInfo method, and returns a single IObservable when its Execute method is called.
public class YahooFinanceStocksQueryProvider : QueryProvider { private YahooFinanceStockService service = new YahooFinanceStockService(); public override string GetQueryText(Expression expression) { return "YahooFinance.Stocks"; } public override object Execute(Expression expression) { /* Normally this would contain generic logic to * parse the query and make the appropriate call to the * service. That's outside of the scope of this article so * we'll just make a call to the service with constant data. */ return service.GetStockInfo("MSFT", new DateTime(1999, 1, 1), DateTime.Now); } }
Now that we have a query provider let's create a query object that we can expose. We'll start with the Query class from the Webward Weblog series but we'll make a few small changes:
- We'll rename the class from Query to ObservableQuery.
- In addition to IQueryable our ObservableQuery class will also implement IObservable.
- If the ObservableQuery object is traversed synchronously (foreach) we'll convert the IObservable object returned by the query provider to an IEnumerable.
public class ObservableQuery<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable, IObservable<T> { internal QueryProvider provider; internal Expression expression; // snip... public IEnumerator<T> GetEnumerator() { return ((IObservable<T>)this.provider.Execute(this.expression)).ToEnumerable().GetEnumerator(); } public IDisposable Subscribe(IObserver<T> observer) { return ((IObservable<T>)this.provider.Execute(this.expression)).Subscribe(observer); } }
"Hold on a second. Why bother to change the query provider to return an IObservable when we can just do the reverse? We could just convert the IEnumerable to an IObservable if our Query object was traversed asynchronously?"
We don't want to do this because converting an asynchronous operation to an IEnumerable uses a precious resource: a thread. Let's say our query provider makes several calls to our web service, one for each stock in the query. We would hope that it would make these calls concurrently. If the query provider had to return an IEnumerable it would need to block and wait for the results from the worker threads. If we turn around and immediately convert that IEnumerable into an IObservable a thread is completely wasted. Therefore to conserve threads we should avoid converting an IObservable to an IEnumerable until we absolutely have to.
Now we're ready to write the Yahoo Finance class that we will query against.
public class YahooFinance { public YahooFinance() { this.Stocks = new ObservableQuery<StockInfo>(new YahooFinanceStocksQueryProvider()); } public ObservableQuery<StockInfo> Stocks { get; internal set; } }
Now if we wrote a query against our Yahoo Finance class and compiled the code we would get the compiler error "Multiple Implementations of the Query Pattern were found for the source type." The C# compiler would be confused because ObservableQuery implements both IEnumerable and IObservable and it wouldn't know which set of extension methods to invoke. The answer is to implement the query pattern for our concrete ObservableQuery class because the compiler will always choose the methods from the most derived class.
public static class ObservableQuery { public static ObservableQuery<T> Where<T>(this ObservableQuery<T> q, Expression<Func<T, bool>> predicateExpression) { MethodInfo method = typeof(ObservableQuery).GetMethods().Where(m => m.Name == "Where" && m.GetParameters().Length == 2).First(); method = method.MakeGenericMethod(new Type[] { typeof(T) }); var expression = Ex.Call(method, q.expression, predicateExpression); return new ObservableQuery<T>(q.provider, expression); } public static ObservableQuery<R> Select<T,R>(this ObservableQuery<T> q, Expression<Func<T, R>> predicateExpression) { MethodInfo method = typeof(ObservableQuery).GetMethods().Where(m => m.Name == "Select" && m.GetParameters().Length == 2).First(); method = method.MakeGenericMethod(new Type[] { typeof(T), typeof(R) }); var expression = Ex.Call(method, q.expression, predicateExpression); return new ObservableQuery<R>(q.provider, expression); } // etc, etc, etc... }
Every time an ObservableQuery extension method is called we append the method call to the expression created so far! In cases where a method accepts a function as a parameter (ex. Select, Where, OrderBy) we accept an expression of that function type instead. This ensures that we don't lose any of the information about the query expression as we're building it. The end result of our query is an ObservableQuery that knows exactly what expression it must execute.
Asynchronously Iterating our Observable Query
Query expressions against an ObservableQuery return a new ObservableQuery - which is an IObservable. Iterating its results is just a matter of subscribing to it.
YahooFinance service = new YahooFinance(); IQueryable stockInfos = from stock in service.Stocks where stock.Name == "MSFT" where stock.Date > new DateTime(1999, 1, 1) select stock; stockInfos.Subscribe(stock => Debug.WriteLine(stock));
On the other hand if we want to traverse the results synchronously we can use foreach...
foreach(StockInfo stock in stockInfos) { Debug.WriteLine(stock); }
Asynchronous Queryable Services
That's it. Hopefully existing query providers will be updated to use this approach so that they can support asynchronous iteration.
Thursday, July 30, 2009
The Joy of Rx: The Event-based Async Pattern vs. IObservable
In part 1 we talked about how to convert events to IObservables. However Rx isn't just about querying events, it's also about querying asynchronous operations.
There are two common asynchronous patterns in the .NET framework: the event-based asynchronous pattern and Begin/EndInvoke. Currently MSDN recommends using the event-based asynchronous pattern where possible. This recommendation may or may not change when IObservable is released with .NET 4.0. I want to make clear that the opinions expressed in this post are my own personal views and are not consistent with official MSDN guidance.
Don't use the event-based asynchronous pattern. Or if you must use the event-based asynchronous pattern be sure to also provide a Begin/EndInvoke version of the API that uses IAsyncResult. Why? Because the event-based asynchronous pattern is deeply flawed. To understand why let's examine a typical piece of code that uses the event-based asynchronous pattern.
Guid token = Guid.NewGuid(); var webClient = new WebClient(); // We need to refer to the identifier within the body // of the method so we must first initialize it to null. // This means we can't use type inference to avoid // having to clutter our code with this absurdly long // (and entirely unnecessary) delegate type. DownloadStringCompletedEventHandler handler = null; handler = (o, a) => { if (((Guid)a.UserState) != token) return; // unhook from the event so that we don't keep firing // after we've gotten our data asynchronously. webClient.DownloadStringCompleted -= handler; if (a.Error != null) { // Handle exception. This may include throwing but // we may also have to invent some method of manually // propagating it if we don't have the knowledge to // handle it at this point and we are in the middle of several //other asynchronous operations. } if (!a.Cancelled) { Debug.WriteLine(string.Format("The downloaded HTML is {0}.", a.Result)); } } // Notice that the only link between the method and the event that returns // its data is a simple naming convention. There's no way to know for _sure_ // which event will return a method's data. client.DownloadStringCompleted += handler; client.DownloadStringAsync(new Uri("http://www.jeffwilcox.com"), token);
A thoroughly awful piece of code no? The good news is that by adding an extension event to the WebClient class we can wrap the event-based asynchronous pattern in an IObservable. Using Rx works around all of the the issues pointed out in the comments above.
Of course it's rather cumbersome to create a new class that inherits from IObservable whenever we need to return the result of an asynchronous operation. It also seems a little silly given that IObservable only has one method: Subscribe. Given this fact it's helpful to create an AnonymousObservable class which accepts an action and invokes it when the Subscribe method is called.
internal class AnonymousObservable<T> : IObservable<T> { private Func<IObserver<T>, IDisposable> subscribeAction; public AnonymousObservable(Func<IObserver<T>, IDisposable> subscribeAction) { this.subscribeAction = subscribeAction; } public IDisposable Subscribe(IObserver<T> observer) { return subscribeAction(observer); } }
Now that we've got our handy, reusable AnonymousObservable class we're ready to create our GetDownloadString extension event.
public static class WebClientExtensions { // No need to put Async in the method title, it's implicit given that // an observable is being returned. public static IObservable<string> GetDownloadString(this WebClient client, Uri address) { // Delay action by nesting it in an observable. // Nothing should ever happen until a client subscribes to the // observable, just as nothing should happen until an // IEnumerable is traversed. return new AnonymousObservable<string>( observer => { // Several downloads may be going on simultaneously. // The token allows us to establish that we're retrieving // the right one. Guid token = Guid.NewGuid(); var stringDownloaded = Observable.FromEvent<DownloadStringCompletedEventArgs>(client, "DownloadStringCompleted") // Confirm its our download using captured state variable .Where(evt => ((Guid)evt.EventArgs.UserState) == token) .Take(1); //implicitly unhooks handler after event is received bool errorOccurred = false; // Subscribe to the IObservable. Under the hood Rx // creates an anonymous observer class that invokes // the three actions passed to the Subscribe method. IDisposable unsubscribe = stringDownloaded.Subscribe( // OnNext action ev => { // Propagate the exception if one is // reported. After this, all observers // will continue to propagate this // exception via the OnError method call // until it is caught. if (ev.EventArgs.Error != null) { errorOccurred = true; observer.OnError(ev.EventArgs.Error); } else if (!ev.EventArgs.Cancelled) { observer.OnNext(ev.EventArgs.Result); } }, // OnError action (propagate exception) ex => observer.OnError(ex), // OnCompleted action () => { // No need to call OnCompleted if // there has been an exception. // It is implicit that there will // be no more data. if (!errorOccurred) { observer.OnCompleted(); } }); client.DownloadStringAsync(address, token); return unsubscribe; }); } }
Granted converting the event-based asynchronous pattern to IObservable requires a little bit of code, but the complexity is pushed onto the library developer. Look at how easy it is to consume the IObservable API.
var webClient = new WebClient(); webClient .GetDownloadString(new Uri("http://www.jeffwilcox.com")) .Subscribe(html => Debug.WriteLine(html));
No need to fuss with event argument objects. The IObservable/IObserver objects take on the concerns of cancelling and propagating exceptions. The developer is given exactly what he or she expects: an asynchronously downloaded string containing the contents of a web page.
Building Complex Queries by Combining Asynchronous Operations
Now that we've got our handy GetDownloadString extension method we can build some pretty complex queries with it. Let's use it to download the HTML from the first ten results pages of a Bing search at once.
public static class EnumerableExtensions { // This is a very handy extension method missing from Linq. public static IEnumerable<T> Iterate<T>(T initalValue, Func<T, T> next) { yield return initalValue; while (true) { initalValue = next(initalValue); yield return initalValue; } } } // snip... var client = new WebClient(); var uri = "http://www.bing.com/search?q=Rx+framework&first={0}&FORM=PERE3"; var pageSize = 10; var resultIndices = EnumerableExtensions.Iterate(1, prev => prev + pageSize).Take(10); var pageDownloads = resultIndices .Select( resultIndex => client .GetDownloadString(new Uri(string.Format(uri, resultIndex))) // append the result index to the results so we can sort by it later .Select(html => new { ResultIndex = resultIndex, Html = html })) // Merge all of the observables into one so that we can subscribe to all of // them simultaneously. .Aggregate( (simultaneousDownloads, currentDownload) => Observable.Merge(simultaneousDownloads, currentDownload)); var concatenatedHtml = pageDownloads // Convert to a blocking enumerable. It makes no sense for // IObservable to have a GroupBy or OrderBy method because // both these methods must block until all the data is received. // However all of the downloads will begin simultaneously when // we traverse the IEnumerable .ToEnumerable() // Order by the result index .OrderBy(pageDownload => pageDownload.ResultIndex) // Grab the HTML .Select(pageDownload => pageDownload.Html) // Concatenate the HTML strings .Aggregate((accumulatedHtml, html) => accumulatedHtml + html) // Grab the first (and only) result. .First();
Pretty slick right? There's only one problem. This code doesn't work :-(. It will block forever. The problem is that the event-based asynchronous pattern always returns on the thread it originates from and that thread is blocked by the call to GetEnumerator().
In the past returning on the UI thread was convenient because developers didn't have to worry about cross-thread accesses. With Rx hopping threads is so easy that cross-thread access isn't much of a concern.
var uiContext = SyncrhonizationContext.Current; AsyncAction.Post(uiContext).Subscribe(() => Debug.Write("Now I'm back on the UI thread!"));
Unfortunately the fact that the event-based async pattern hops the the UI thread makes it much more difficult to compose a set of asynchronous operations together and block on the result. This is why it's important to always provide a Begin/End Invoke version of your event-based, asynchronous API's.
WebClient doesn't provide a Begin/End overload for DownloadStringAsync so to work around the blocking problem we'll have to create an non-blocking OrderBy method for IObservable.
public static class ObservableExtensions { public static IObservable<T> OrderBy<T, R>(this IObservable<T> that, Func<T, R> keySelector) { return new AnonymousObservable<T>( observer => { var orderBySubject = new OrderBySubject<T, R>(keySelector); var unsubscribe = orderBySubject.Subscribe(observer); that.Subscribe(orderBySubject); return unsubscribe; }); } public class OrderBySubject<T,R> : Subject<T> { private Func<T,R> keySelector { get; set; } public OrderBySubject(Func<T, R> keySelector) { this.keySelector = keySelector; } List<T> list = new List<T>(); public override void OnNext(T value) { list.Add(value); } public override void OnCompleted() { foreach (var item in list.OrderBy(keySelector)) { base.OnNext(item); base.OnCompleted(); } } } }
Now we can rewrite the code above asynchronously.
// snip... var concatenatedHtml = pageDownloads // Order by the result index .OrderBy(pageDownload => pageDownload.ResultIndex) // Grab the HTML .Select(pageDownload => pageDownload.Html) // Concatenate the HTML strings .Aggregate((accumulatedHtml, html) => accumulatedHtml + html) // Write the first (and only) result. .Subscribe(html => Debug.Write(html));
Of course I could've avoided the blocking problem by using the WebRequest object because it has Begin/End methods. However the point of this article was to show that although the event-based asynchronous pattern can be converted to an IObservable the abstraction leaks.
Think Ahead When Designing Asynchronous APIs
If Rx is embraced (which I expect it will be) existing event-based asynchronous APIs will be more cumbersome to work with than those written using the alternative Begin/End Invoke pattern. I ask developers to consider this when designing asynchronous APIs today.
The Joy of Rx: Extension Events
Based on the logs it seems there is tremendous interest in Rx, the new .NET 4.0 interfaces that allow you to query events and asynchronous operations. I can't say I'm surprised. In my opinion Rx is the big story of .NET 4.0. I've been developing software using Rx for months now and I thought I'd share some tips and tricks that I've picked up with the community. You can get the Sivlerlight version of Rx from the binaries folder in the Silverlight Toolkit sources.
Converting Events to IObservables
One of the cumbersome things about Rx development is that events must be converted to IObservable's before they can be queried. Rx provides a static FromEvent method to help with the conversion:
var mouseMove = Observable.FromEvent<MouseEventArgs>(Application.Current.RootVisual, "MouseMover"); mouseMove.Subscribe(() => Debug.WriteLine("the mouse has been moved.");
Unfortunately putting event names (or any identifiers for that matter) in strings breaks our refactoring tools. Rx does provide another overload which accepts two actions, one that attaches the handler and another that detaches it...
class MyClass { public EventHandler<RoutedEventArgs> MyEvent; } var myClass = new MyClass(); var myClassEvent = Observable.FromEvent<RoutedEventArgs>(handler => myClass.MyEvent += handler, handler => myClass.MyEvent -= handler); mouseMove.Subscribe(() => Debug.WriteLine("the mouse has been moved.");
...but not only is this overload rather more verbose, it only works if the delegate type of your event is the generic EventHandler<T>. Unfortunately most events use custom delegate types because in .NET 1.0 generics did not exist.
**Updated: There is an overload which I missed that accepts the delegate type:
var myButton = new Button(); var myButton = Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(handler => myClass.Click += handler, handler => myClass.Click -= handler);
So what's the best way of exposing events as IObservables?
Extension Events
If you're doing Rx development in C# it's good practice to create extension methods for each event you would like to query. You can think of these methods as extension events. To demonstrate let's create a static class with extension methods that expose the events on Silverlight/WPF's UIElement class as IObservables.
internal static class UIElementExtensions { public static IObservable<Event<MouseButtonEventArgs>> GetMouseLeftButtonDown(this UIElement that) { return Observable.FromEvent<MouseButtonEventArgs>(that, "MouseLeftButtonDown"); } public static IObservable<Event<MouseButtonEventArgs>> GetMouseLeftButtonUp(this UIElement that) { return Observable.FromEvent<MouseButtonEventArgs>(that, "MouseLeftButtonUp"); } public static IObservable<Event<MouseEventArgs>> GetMouseLeave(this UIElement that) { return Observable.FromEvent<MouseEventArgs>(that, "MouseLeave"); } public static IObservable<Event<MouseEventArgs>> GetMouseEnter(this UIElement that) { return Observable.FromEvent<MouseEventArgs>(that, "MouseEnter"); } }
Unfortunately our event names are stored in strings, but at least they are in one place. Now that we've created extension methods that expose IObservables we can create more complex events by sequencing these primitive events.
Sequencing Events
The most exciting thing about Rx is that it enables you build complex events from a sequence of primitive events. Let's create an observable that fires when the sequence of keys "a", "b", "c" is pressed:
// create an event that listens for key presses and returns the // key pressed. IObservable<Key> keyPress = Observable.FromEvent<KeyEventArgs>(Application.Current.RootVisual, "KeyUp") .Select(ev => ev.EventArgs.Key); // Create a helper function for creating observables that fire // when a specific key is pressed. Func<Key, IObservable<Key>> pressedIs = key => keyPress.Where(pressedKey => pressedKey == key); // Create a helper function for creating observables that fire // when a key other than a specific key is pressed. Func<Key, IObservable<Key>> pressedIsNot = key => keyPress.Where(pressedKey => pressedKey != key); IObservable<Unit> abcPressed = // Always listen for a key press "A" because it is the start of the sequence. // Each time the "A" key is pressed we being matching the sequence again. from firstKeyPressEvent in pressedIs(Key.A) // After "A" is pressed when only want to wait for a single "B" key press. // If any other key is pressed we start at the beginning and wait for "A" from secondKeyPressEvent in pressedIs(Key.B).Take(1).Until(pressedIsNot(Key.B)) // After "B" is pressed when only want to wait for a single "C" key press. // If any other key is pressed we start at the beginning and wait for "A" from thirdKeyPressEvent in pressedIs(Key.C).Take(1).Until(pressedIsNot(Key.C)) // I could return the string "abc" here but we know what exactly what keys // were pressed because this event is so specific. I really don't have anything // to return but all queries must return something. In cases like this we return the // Unit value. It's like returning null, but it has a type and can't cause a null // reference exception. select new Unit(); abcPressed.Subscribe(()=> Debug.WriteLine("ABC was pressed."));
Just as in Linq to Objects, multiple uses of the "from" keyword are translated into nested calls to the SelectMany extension method. The abcPressed query above could also be coded this way:
IObservable<Unit> abcPressed = pressedIs(Key.A) .SelectMany( firstKeyPressEvent => pressedIs(Key.B).Take(1).Until(pressedIsNot(Key.B)) .SelectMany( secondKeyPressEvent => pressedIs(Key.C).Take(1).Until(pressedIsNot(Key.C)))) .Select(_ => new Unit());
Building a "Click" Extension Event
Now that we know how to sequence events let's use Rx to add a "Click" extension event to all instances of UIElement - a class from which all Controls inherit. The sequence of events that constitute a click event are a little more complex than one might think. In a nutshell we consider a click event on UIElement "A" has occurred if we match this sequence of events...
1. MouseLeftButtonDown over UIElement "A"
2. MouseLeftButtonUp over UIElement "A"
...or this sequence of events...
1. MouseLeftButtonDown over UIElement "A"
2. MouseLeave UIElement "A"
3. MouseEnter UIElement "A"
4. MouseLeftButtonUp over UIElement "A"
...but NOT this sequence of events:
1. MouseLeftButtonDown over UIElement "A"
2. MouseLeave UIElement "A"
3. MouseEnter UIElement "B"
3. MouseLeftButtonUp over UIElement "B"
3. MouseLeftButtonDown over UIElement "B"
2. MouseLeave UIElement "B"
3. MouseEnter UIElement "A"
4. MouseLeftButtonUp over a UIElement "A"
Let's add a GetClick extension event to the UIElementExtensions class we defined earlier.
public static IObservable<Event<MouseButtonEventArgs>> GetClick(this UIElement that) { return that // wait for any mouse left down event .GetMouseLeftButtonDown() .SelectMany( mouseLeftButtonDownEvent => // then wait for a single mouse left up event that .GetMouseLeftButtonUp() .Take(1) .Until( // We want to merge two different stop conditions... Observable.Merge( // stop listening if the mouse goes outside // the silvleright plug-in Application.Current.RootVisual.GetMouseLeave() // We return unit so that we have the // same type as the other observable // we want to merge with .Select(_ => new Unit()), // stop listening if the mouse goes outside the // element and the mouse is released. that .GetMouseLeave() .SelectMany( mouseLeaveEvent => // stop waiting for a mouse left up event // if the mouse leaves the element and the // button is released. // By listening for the event at the Root // Visual we ensure that we will get all // MouseLeftButtonUp events because this // event bubbles up. Application.Current.RootVisual .GetMouseLeftButtonUp() .Take(1) // Return unit so that we can merge .Select(_ => new Unit()) // don't cancel if the mouse enters // the element over which the mouse // was depressed. .Until(that.GetMouseEnter()))))); }
That's it! Now every single instance of UIElement has a Click event we can subscribe to:
var rectangle = new Rectangle { Width = 100, Height = 100, Fill = new SolidColorBrush(Colors.Red) }; rectangle.GetClick().Subscribe(() => Debug.WriteLine("The rectangle was clicked.");
More Reliable Code, and Less of It
Rx allows you to write complex, asynchronous code declaratively. If you master it you'll never have to explicitly unhook a handler from an event again. You also wont ever have to maintain an error-prone collection of state variables in order to ascertain whether a sequence of events has occurred in a particular order.
*Edit: Here's the code!
Wednesday, July 22, 2009
Introducing Rx (Linq to Events)
It’s the most wonderful time of the year: a new version of the Silverlight Toolkit has been released alongside Silverlight 3. This release of the Toolkit has a lot of goodies including a new TreeMap control, a Rating control (written by yours truly), and a useful collection of extensions methods for TreeView. That said this post is not really about the Toolkit. Buried deep in the bin folder of the Silverlight Toolkit Unit Tests is a hidden gem: The Rx Framework (System.Reactive.dll). If you glanced quickly you’d miss it altogether but it’s one of the most exciting additions to the .NET framework since Linq.
Stating the Obvious: Asynchronous Programming is Hard
Developers tend to avoid asynchronous programming if possible because it makes our programs non-deterministic and obscure our code’s intent in a sea of callbacks. However the truth is that asynchronous programming has become an essential part of application development. Client apps have always needed to use asynchronous methods to keep the user interface responsive. As a matter of fact Silverlight developers don’t have any choice in the matter because Silverlight doesn’t include any blocking IO calls. Connected apps also need to take advantage of asynchronous programming to improve scalability. Mashups, apps cobbled together from web services, often need to retrieve data from multiple sources in a particular order.
Clearly we need to write asynchronous code to create modern, connected applications, but how to do it in such a way that our code remains clear and maintainable?
Introducing the Rx Framework
The IEnumerable Interface
We’re all familiar with the IEnumerable interface. Almost every collection implements it and we use it every time we write a foreach. Most of us are also pretty comfortable using Linq to query IEnumerables. Linq is a series of extension methods used to manipulate sequences. Here’s a simple example:
int[] numbers = new int[]{20,31,5,16,22}; IEnumerable<int> numbersSmallerThan20 = numbers.Where(number => number < 20);
The resulting numbersSmallerThan20 sequence looks like this when visualized:
5, 16, break
In addition to finite sequences it can sometimes be useful to create sequences that never end. Take this method that returns an infinite sequence of integers:
IEnumerable<int> NaturalNumbers() { int number = 0; while(true) { yield return number; number++; } }
When visualized this sequence looks like this:
0,1,2,3,4,5,6…
Enumerable’s are sequences of data that we pull from a data source. We don’t always pull data though. Often it is pushed onto us and we must react appropriately. This is called “Reactive Programming.”
Reactive Programming
Reactive programs are ubiquitous. We use reactive programming every time we register a handler with an event or a specify a call back for an asynchronous operation. In this example I register a handler for the mouse move event of a button. The handler prints out the location of the mouse when it is called.
button.MouseMove += (o, mouseEventArgs) => Debug.Writeline(“You moved the mouse to {0}”, mouseEventArgs.GetPosition(button));
“You moved the mouse to 20,3”
“You moved the mouse to 33,12”
“You moved the mouse to 44,18”
Another form of reactive programming is running an asynchronous method and passing it a callback method to invoke when it’s finished. This is how we keep our programs responsive during long-running operations. Here’s an example:
DownloadFile(“http://www.jeffwilcox.com”, (byteArray) => Debug.WriteLine(“This file is {0} bytes long.”, byteArray.Length);
“This file is 12323 bytes long.”
We’re all used to doing reactive programming by specifying methods that are called at unpredictable times, but what if there was a different way to think about reactive programming? What if we thought of each piece of data passed to a reactively-called method as an item in a sequence?
Events and Callbacks are Sequences of Data!
The data passed to event handlers and callbacks can be thought of as sequences of data that are “pushed” at you rather than “pulled.” Every time an event is fired we get “pushed” a new piece of data: the EventArgs. Similarly when a callback is invoked it is typically “pushed” the result of the asynchronous method. You can think of an event as a sequence of EventArgs that never ends just like the NaturalNumbers sequence. If you were to visualize the mouse move event as a sequence it would look like this:
new MouseEventArgs(new Point(20,3)), new MouseEventArgs(new Point(33,12)), new MouseEventArgs(new Point(44,18))…
Similarly the asynchronous DownloadFile method can be viewed as a “push” sequence of data with only one entry:
new byte[]{23,211,33,23…}, break
The Rx team has discovered that pull sequences and push sequences are “dual.” That is to say, any operation you can perform on “pull” sequences can also be performed on “push” sequences. This is quite a revelation. To put things in perspective it’s been 13 years since Design Patterns was published and we’ve only now realized that the Observable pattern and the Iterator pattern are actually the same pattern.
Although reactive “push” sequences are fundamentally the same as “pull” sequences the IEnumerable interface can’t be used for reactive sequences because it blocks. We need a new interface with non-blocking methods that correspond exactly to the blocking methods on IEnumerable. We need…
The IObservable/IObserver Interface
Despite the fact that they may look somewhat different on the surface the IObservable/IObserver pair of interfaces are the non-blocking equivalents of IEnumerable/IEnumerator.
To help you understand how they are equivalent let’s take a look at a simple example of traversing a pull sequence. You enumerate an IEnumerable by requesting an IEnumerator. As you call MoveNext the IEnumerator “pulls” data from the IEnumerable, usually by invoking its methods.
IEnumerator<int> numberEnumerator = new int[]{1,2,3}; while(numberEnumerator.MoveNext()) { Debug.WriteLine(“{0}”, numberEnumerator.Current); } Debug.WriteLine(“all done.”);
This prints:
1 2 3 all done.
To traverse an IObservable you go through the same actions as an IEnumerable but in reverse. You create an IObserver, give it to an IObservable, and the IObservable “pushes” data into the IObserver by invoking its methods. When an IObservable invokes the “OnNext” method on an Observer it is equivalent to an IEnumerable method using the yield keyword to give information to an IEnumerable. Similarly when an IObservable invokes the “OnCompleted” method on an Observer it is equivalent to an IEnumerable using the break keyword to indicate that there is no more data.
Let’s define a NumbersObserver which converts a “pull” sequence of numbers into a “push” sequence as well as an observer that listens to our NumbersObserver and prints its contents.
internal class AnonymousDisposable : IDisposable { internal Action Action {get; internal set;} void IDisposable.Dispose() { this.Action(); } } class NumbersObservable : IObservable<int> { public NumbersObservable(IEnumerable<int> numbers) { this._numbers = numbers; } private IEnumerable<int> _numbers; public IDisposable Subscribe(IObserver<int> observer) { foreach(int number in _numbers) { observer.OnNext(number); } observer.OnCompleted(); return new AnonymousDisposable { Action = () => { ; // do nothing because we’ve already called OnCompleted() } }; } } class DebugObserver : IObserver<int> { public void OnNext(int value) { Debug.WriteLine(“{0}”, value); } public void OnCompleted() { Debug.WriteLine(“all done.”); } public void OnError(Exception ex) { Debug.WriteLine(“Whoops exception, I’d better throw.”) throw ex; } }
Now let’s use these classes to create a “push” version of our “pull” example:
new NumbersObservable(new[]{2,3,4}).Register(new DebugObserver());
This prints…
1 2 3 all done.
“Whoa, whoa! Isn’t this pretty complicated?”
Don’t worry. This example above is just to demonstrate the interplay between the interfaces. Most of the time you won’t have to implement your own Observable or Observer. Rx includes lots of methods for constructing observables and observers. Using Rx extension methods I can rewrite the code above like so:
new[]{1,2,3}.ToObservable().Subscribe(number => Debug.WriteLine(“{0}”, number));
“What’s with the IDisposable object?”
The IDisposable object is returned by an Observable when you register an observer with it. When you invoke the Dispose method on the registration object the observer will stop listening to the observable for data. This is the active equivalent of passively not calling MoveNext() anymore in the middle of a sequence. Rather than invoke the Dispose method directly you will most often have it invoked for you by Rx. In the following example the TakeWhile method will invoke Dispose under the hood to detach from an observable as soon as a number larger than 10 is returned.
var numbersSmallerThanTen = Enumerable.Range(0,100).ToObservable().TakeWhile(x => x <= 10);
Here’s an example of invoking Dispose explicitly to detach from an event which has been converted to an Observable:
// Use an Rx method to convert an event to an Observable IObservable<Event<MouseEventArgs>> mouseMoveEventObservable = Observable.FromEvent<MouseEventArgs>(myControl, “MouseMove”); // register a handler with the event using an overload that accepts a lambda instead of an Observer IDisposable registration = mouseMoveEventObservable.Subscribe(mouseMoveEvent => Debug.Write(“The mouse was moved.”)); // stop listening to the event registration.Dispose();
“What about the OnError method? I don’t see an equivalent for that in IEnumerable or IEnumerator either.”
When errors occur in asynchronous operations the exception must be passed to the callback method so that the callback method can handle it. That’s why this method exists in IObservable but seems to have no equivalent in IEnumerable. In IEnumerable it is implicit because you can use try/catch.
Linq to IObservable
Now that we understand that an IObservable is just a “push” version of IEnumerable it just be obvious that all of the familiar Linq methods apply to it. In fact it is equally appropriate to use query syntax on “pull” sequences and “push” sequences. Both are queries in the strictest sense and the fact that a sequence is push or pull is orthogonal. Let’s analyze a typical Linq query:
IEnumerable<Point> points = from x in Enumerable.Range(0, 2) from y in Enumerable.Range(0, 2) select new Point(x,y);
A verbal description of this query might be:
“For each x in the sequence [0 to 1] get each y in the sequence [0 to 1] and create a new point for each pair of values.”
The result of course is:
0,0 0,1 1,0 1,1
Now let’s contrast this with an Rx query that creates a dragging event for a Silverlight/WPF control:
IObservable<Event<MouseEventArgs>> draggingEvent = from mouseLeftDownEvent in control.GetMouseLeftDown() from mouseMoveEvent in control.GetMouseMove().Until(control.GetMouseLeftUp()) select mouseMoveEvent;
A verbal description of this query might be:
“For each mouse left down event, get each mouse move event and return it until the next mouse left up event occurs.”
As you can see, using “from” allows us to declaratively sequence events. The alternative would be to create a state machine, setting a flag when the mouse button is pressed and then behaving differently when the mouse is moved and that flag is set. With Rx the code for the drag event is self-contained and involves no variable mutation.
With Linq to IEnumerable we transform and combine sequences of data to create a sequence containing exactly the data we need. Then we traverse that sequence and do something with the data. With Linq to IObservable we can transform and combine events and async callbacks to create the precise event we’re interested in. Then we register a handler and do something with the data.
Silverlight Toolkit Unit Test Code Written with Rx
The Silverlight Toolkit team is using Rx to write reliable, event-based asynchronous tests. This is essential as the elements in a control’s visual tree are created asynchronously, forcing us to wait for an event in order to confirm they were created appropriately. Let’s take a look at a test for Rating that uses Rx.
This test ensures that the Actual Value of a RatingItem is %100 when its parent Rating is %100. This is tricky because when you change the value of rating it animates to the new value using an internal storyboard. I have to wait for ActualValue to be animated to the Value property before I examine the RatingItem.
The test creates a Rating control, places it on screen, and waits for LayoutUpdated to ensure that the rating items are generated. Then it asynchronously sets the value of rating to 1.0. As the ActualValue of the Rating changes, the various RatingItems will have their ActualValue’s set accordingly depending on their index. The test needs to wait until the Rating’s ActualValue reaches Value before checking to make sure the ActualValue of the last rating item is 1.0.
Rating rating = new Rating(); IObservable<Unit> test = // Unit is an object that represents null. ObservableExtensions .DoAsync(() => TestPanel.Children.Add(rating)) .WaitFor(TestPanel.GetLayoutUpdated()) // Extension method GetLayoutUpdated converts the event to observable .DoAsync(() => rating.Value = 1.0) // Calls the Ignite EnqueueCallback method .WaitFor( // waits for an observable to raise before going on // listen to all the actual value change events and filters them until ActualValue reaches Value rating .GetActualValueChanged() // extension method that converts ActualValueChanged event to IObservable .SkipWhile(actualValueChangedEvent => actualValueChangedEvent.EventArgs.NewValue != rating.Value)) // check to make sure the actual value of the rating item is set appropriately now that the animation has completed .Assert(() => rating.GetRatingItems().Last().ActualValue == 1.0) // crawls the expression tree and makes a call to the appropriate Assert method Test.Subscribe(() => TestPanel.Children.Remove(rating)); //run the test and clean up at the end.
The code above uses a variety of extension methods we built to manipulate observable objects. You can use these libraries in your own unit tests by downloading the Silverlight Toolkit sources.
Always Useing IObservable for New Asynchronous APIs
The IObservable/IObserver interfaces are in .NET framework 4.0. I want to stress that IObservable is the new asynchronous programming pattern in .NET. It supplants the Begin/EndInvoke pattern as well as the event-based asynchronous pattern. Simple run of thumb: if the method is asynchronous, return an IObservable. *
*A correction here. It is still perfectly acceptable to use Begin/End Invoke or the event-based asynchronous pattern. Large portions of the framework use these patterns and will continue to do so for the sake of consistency.
Exposing IObservable is like putting lighting in a bottle. Developers can open it up and get access to a galaxy of Linq methods they can use to combine and sequence them with other IObservables. My hope is that eventually API’s exposing IObservable will be just as common as those exposing IEnumerable.
Erik Meijer Strikes Again
Rx is the brainchild of Erik Meijer, the father of Linq and recent recipient of the Outstanding Technical Leadership award at Microsoft. Erik is the reason I chose to work for Microsoft. With the introduction of Rx and his work on Linq and Haskell he has profoundly changed the way I approach software development twice in four years – an incredible feat. Thanks to Meijer, Microsoft does a better job than anyone of taking bleeding-edge functional programming research and productizing it. The Rx team also includes my favorite blogger, Wes Dyer whose blog posts opened my eyes to what an incredibly versatile language C# is.
After using it for the last few months it’s now impossible to imagine doing Silverlight development without Rx. As of today the only place you can get it is in the Silverlight Toolkit sources. Take a look. If you are comfortable with Linq programming you’ll find it to be extremely powerful.
Blog Archive
About Me
- Jafar Husain
- I'm a software developer who started programming at age 16 and never saw any reason to stop. I'm working on the Presentation Platform Controls team at Microsoft. My primary interests are functional programming, and Rich Internet Applications.