Time for a little more Rx love. Today we’re going to call a long running function asynchronously and handle the return on the Dispatcher thread almost as simply as calling the function directly.
void CallSomeLongFunctionAsync()
{
Observable
.ToAsync<int>(LifeTheUniverseEverything)()
.ObserveOnDispatcher()
.Subscribe(theAnswer => MessageBox.Show(string.Format("The answer was {0}. What was the question anyway?", theAnswer)));
}
private int LifeTheUniverseEverything()
{
Thread.Sleep(600000);
return 42;
}
I may have used my new-found powers to calculate the answer to life, the universe, and everything but imagine a service call or long running file operation. Making those UIs completely non-blocking just got a lot easier.
Code Samples
Rx
I’ve been playing with Rx (Reactive Extensions) quite a bit lately and I have to say it’s one of the most powerful features added to .Net since LINQ. That probably isn’t too surprising since a lot of people call Rx “LINQ to Events”.
One common task when creating UIs is to call some function, like a filter or search, when the user stops typing. Anyone who has set this up before knows how painful it is but with Rx it’s dead simple.
private IDisposable textBoxObserver;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
textBoxObserver =
Observable
.FromEvent<TextChangedEventArgs>(SomeTextBox, "TextChanged")
.Throttle(TimeSpan.FromMilliseconds(500))
.ObserveOnDispatcher()
.Subscribe(HandleSomeTextBoxTextChanged);
}
void MainWindow_Unloaded(object sender, RoutedEventArgs e)
{
textBoxObserver.Dispose();
}
private void HandleSomeTextBoxTextChanged(IEvent<TextChangedEventArgs> args)
{
var tb = ((TextBox)args.Sender);
var text = tb.Text;
SomeFunctionYouWantToCallWhenTheUserStopsTyping(text);
}
There are a lot of other really cool uses for Rx so I’ll be posting more soon. Until then you should check out 101 Rx Samples or this simple implementation of drag & drop in Silverlight.
Code Samples
Rx
You know how sometimes you run into problems that you know should be really simple but you just can’t manage to solve them? Well one of those problems for me was making a grid an ItemsHost and actually having it work. Sure you can always set IsItemsHost=”True” but you’ll just end up with every item in your ItemsSource all stacked on top of each other. A few weeks ago I ran into this same problem again and couldn’t find a way around it. So after beating my head against the wall for a few hours I finally gave up and posted on StackOverflow, within a half hour I had the answer I’d been trying to figure out for the better part of a day.
It turns out that setting IsItemsHost to True on a panel creates the required ContentPresenters and nothing you can do will let you get at them directly (believe me, I tried). The solution is to create a keyless style that targets ContentPresenter and let this style take care of the dirty work.
Without furthur ado, here’s an example.
The Data
Class DataPoint
Private myRow As Integer
Public Property Row() As Integer
Get
Return myRow
End Get
Set(ByVal value As Integer)
myRow = value
End Set
End Property
Private myCol As Integer
Public Property Col() As Integer
Get
Return myCol
End Get
Set(ByVal value As Integer)
myCol = value
End Set
End Property
Private myText As String
Public ReadOnly Property Text() As String
Get
Return myText
End Get
End Property
Public Sub New(ByVal x As Integer, ByVal y As Integer)
Me.Row = y
Me.Col = x
myText = "Column " + x.ToString + ", Row " + y.ToString
End Sub
End Class
Load the test data and set it as the data context for the window
Class GridAsItemsHostExample
Private myTestData As TestData
Public Sub New()
InitializeComponent()
myTestData = New TestData()
Me.DataContext = myTestData
End Sub
End Class
Class TestData
Private myDataSet As List(Of DataPoint)
Public Property DataSet() As List(Of DataPoint)
Get
Return myDataSet
End Get
Set(ByVal value As List(Of DataPoint))
myDataSet = value
End Set
End Property
Public Sub New()
Me.DataSet = New List(Of DataPoint)
For x As Integer = 0 To 1
For y As Integer = 0 To 1
Me.DataSet.Add(New DataPoint(x, y))
Next
Next
End Sub
End Class
And finally the window, note the ContentPresenter style.
<Window x:Class="GridAsItemsHostExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Grid As ItemsHost Example"
SizeToContent="WidthAndHeight">
<ItemsControl ItemsSource="{Binding DataSet}">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Column" Value="{Binding Col}" />
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Margin" Value="5" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Style>
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Grid HorizontalAlignment="Stretch"
IsItemsHost="True"
ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Style>
</ItemsControl>
</Window>
Code Samples
Grid, ItemsHost, WPF, xaml