How could I animate one stack panel item at a time

2019-07-13 01:49发布

Say I have a StackPanel with a few items in it. When I load my view, I want to apply some animation to them. But I want each item to animate in sequence, one after the other. How would I go about doing this? In particular, is there a clean way to do this in an MVVM framework?

Edit - I should mention that the items are databound to the StackPanel through an ItemsControl, which might make things substantially more difficult. It looks like this

                   <ItemsControl x:Name="Items">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Button Content="{Binding ButtonName}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>

Some UI code omitted for clarity.

1条回答
Animai°情兽
2楼-- · 2019-07-13 02:34

Copy/Paste/Compile/Run : I'd throw an explanation but too many topics to cover. Basically the sample shows how to MultiTrigger, Animate, Load Items in the Background without freezing UI and use of PropertyChanged to notify UI. Enjoy.

Here's the XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"
    x:Name="wnd">
<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisibility" />
</Window.Resources>

<ItemsControl x:Name="Items" ItemsSource="{Binding TestItems, ElementName=wnd}" Loaded="Items_Loaded">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button x:Name="item" Content="{Binding DisplayString}" Margin="5">
                <Button.Style>
                    <Style TargetType="{x:Type Button}">
                        <Setter Property="Opacity" Value="0"/>
                        <Setter Property="Visibility" Value="{Binding Path=IsVisible, Converter={StaticResource BoolToVisibility}}"/>
                        <Style.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsLoading}" Value="True"/>
                                    <Condition Binding="{Binding IsVisible}" Value="True"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard >
                                            <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1.5" AccelerationRatio="0.3"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </MultiDataTrigger.EnterActions>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>                   
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Then here's the code behind for it:

using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication1
{

public partial class MainWindow : Window
{
    private List<TestItem> _items;
    public List<TestItem> TestItems
    {
        get
        {
            if(_items == null)
            {
                _items = new List<TestItem>();
                for(int i = 0; i < 10; i++)
                    _items.Add(new TestItem{ DisplayString = i.ToString(CultureInfo.InvariantCulture), IsVisible = true});
            }
                return _items;
        }
    }

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Items_Loaded(object sender, RoutedEventArgs e)
    {
        /*in background so not to freeze the UI thread*/
        Task.Factory
            .StartNew(() =>
                        {
                            foreach (var item in TestItems)
                            {
                                item.IsLoading = true;
                                item.IsVisible = true;
                                /*using sleep as quick and dirty just to slow down loading and show the animation (otherwise it's a no-no )*/
                                Thread.Sleep(500);
                            }
                        }
            );
    }
}

public class TestItem : INotifyPropertyChanged
{
    private string _displayString;
    private bool _isVisible;
    private bool _isLoading;

    public string DisplayString
    {
        get { return _displayString; } 
        set
        {
            if (_displayString == value) return;
            _displayString = value;
            RaisePropertyChanged("DisplayString");
        }
    }

    public bool IsVisible
    {
        get { return _isVisible; }
        set
        {
            if (_isVisible == value) return;
            _isVisible = value;
            RaisePropertyChanged("IsVisible");
        }
    }

    public bool IsLoading
    {
        get { return _isLoading; }
        set
        {
            if (_isLoading == value) return;
            _isLoading = value;
            RaisePropertyChanged("IsLoading");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
}
查看更多
登录 后发表回答