I’ve been working on several applications where I needed to display several items in a ListBox (or an ItemsControl) at startup, but they had to appear on the screen one by one, with a short delay, not all at once. Using ListBoxItem’s layout states took care of handling how an individual item would appear in the list, but I still needed to handle a short pause between each item being added to the list. Usually I resorted to using a Timer, which sorted out that needed delay for me, but that really felt like hacking that had nothing to do with the real problem.
Reactive Extensions, however, offer a much elegant solution. The GenerateFromTime() construction operator is a close relative to the Generate() operator used in my previous blog entry, except GenerateFromTime() adds an important time dimension to generated sequence – the last parameter in this operator lets you specify a delay between each call to OnNext():
private readonly IObservable<string> numbers = Observable.GenerateWithTime(1, i => i <= 8, i => i + 1, i => i.ToString(), i => TimeSpan.FromSeconds(.3));
The above code snippet will produce an observable sequence of 8 strings, progressing through these strings with a 0.3 seconds delay.
The rest of the code:
private void OnLoaded(object sender, RoutedEventArgs e)
{
numbers.ObserveOnDispatcher().Subscribe(AddImage);
}
private void AddImage(string image)
{
list.Items.Add(image);
}
Note the ObserveOnDispatcher() operator again – GenerateWithTime uses a timer operating on a background thread so we need to ensure the AddImage() method is called on the UI thread.
Layout states for this sample are kept really basic:
<VisualStateGroup x:Name="LayoutStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<VisualTransition.GeneratedEasingFunction>
<CubicEase EasingMode="EaseOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="AfterLoaded"/>
<VisualState x:Name="BeforeLoaded">
<Storyboard>
<DoubleAnimation Duration="0" To="-94" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="grid" d:IsOptimized="True"/>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="grid" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="BeforeUnloaded"/>
</VisualStateGroup>
And the final result can be observed by following this link.
This post has shown how to use Reactive Extensions in Silverlight to gradually fill a ListBox, with a bonus of a nice item entry animation, provided by the layout states.
Download source code from here.
c49357a1-93b9-4a54-bd3f-9dc598a5be5c|1|5.0|27604f05-86ad-47ef-9e05-950bb762570c