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.