ListBox item load animation

2019-02-09 17:27发布

问题:

I want to create user control based on ListBox (ListView) with such animation: the items in listbox do not loads all at once, they have to load step-by-step (item-by-item, first then second, then third, etc.) with some timeout between them.

How can I do that?

回答1:

You could use a Blend SDK behavior for this:

<ListBox ItemsSource="{Binding Collection, Source={StaticResource SampleData}}">
    <i:Interaction.Behaviors>
        <b:FadeAnimateItemsBehavior Tick="0:0:0.05">
            <b:FadeAnimateItemsBehavior.Animation>
                <DoubleAnimation From="0" To="1" Duration="0:0:0.3"/>
            </b:FadeAnimateItemsBehavior.Animation>
        </b:FadeAnimateItemsBehavior>
    </i:Interaction.Behaviors>
</ListBox>
class FadeAnimateItemsBehavior : Behavior<ListBox>
{
    public DoubleAnimation Animation { get; set; }
    public TimeSpan Tick { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
    }

    void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        IEnumerable<ListBoxItem> items;
        if (AssociatedObject.ItemsSource == null)
        {
            items = AssociatedObject.Items.Cast<ListBoxItem>();
        }
        else
        {
            var itemsSource = AssociatedObject.ItemsSource;
            if (itemsSource is INotifyCollectionChanged)
            {
                var collection = itemsSource as INotifyCollectionChanged;
                collection.CollectionChanged += (s, cce) =>
                    {
                        if (cce.Action == NotifyCollectionChangedAction.Add)
                        {
                            var itemContainer = AssociatedObject.ItemContainerGenerator.ContainerFromItem(cce.NewItems[0]) as ListBoxItem;
                            itemContainer.BeginAnimation(ListBoxItem.OpacityProperty, Animation);
                        }
                    };

            }
            ListBoxItem[] itemsSub = new ListBoxItem[AssociatedObject.Items.Count];
            for (int i = 0; i < itemsSub.Length; i++)
            {
                itemsSub[i] = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
            }
            items = itemsSub;
        }
        foreach (var item in items)
        {
            item.Opacity = 0;
        }
        var enumerator = items.GetEnumerator();
        if (enumerator.MoveNext())
        {
            DispatcherTimer timer = new DispatcherTimer() { Interval = Tick };
            timer.Tick += (s, timerE) =>
            {
                var item = enumerator.Current;
                item.BeginAnimation(ListBoxItem.OpacityProperty, Animation);
                if (!enumerator.MoveNext())
                {
                    timer.Stop();
                }
            };
            timer.Start();
        }
    }
}

Tick specifies the time between when items are started to fade in. Animation is the animation applied to the Opacity for the fade in, it can be set in Xaml to be very costomizable (e.g. easing functions and fade time).

Edit: Added new item fade in (only works if ItemsSource is used and implements INotifyCollectionChanged)

(Use code snippets like this with caution, if at all. This code is mainly for demonstration purposes and giving a general idea of how this can be approached. This could probably also be done using Blend 4's native FluidMoveBehaviors if availabe.)