WPF / MVVM - ItemsControl中的WrapPanel,添加的子项上的动画

时间:2015-11-17 12:25:58

标签: wpf animation mvvm storyboard transition

假设:

<ScrollViewer VerticalScrollBarVisibility="Auto" >
    <ItemsControl ItemsSource="{Binding Controls}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel />              
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>

其中“控件绑定”是包含某种用户控件的viewmodel中的ObservableCollection。

由于wrappanel中的内容居中,当前行为如下:

  • UC A被添加到列表中,并显示在面板的中央。
  • UC B被添加到列表中,UC A向左移动,UC B被添加到面板。
  • UC C被添加到列表UC A和B中 向左移动,UC C被添加到面板。

我想要的是在添加新的用户控件时添加“移动”转换/转换,即我想在添加每个UC时向左侧显示A / B ......的转换动画。

我更愿意在XAML中尽可能多地做,而不是打破MVVM模式。

enter image description here

奖金,我希望能够在移除UC时进行动画制作。

1 个答案:

答案 0 :(得分:1)

我将Controls视为FrameworkElement的ObservableCollection 您可以使用以下代码:

<Window x:Class="Marathonbet.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="MainWindow_OnLoaded" Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Window.Resources>
        <Storyboard x:Key="OnLoaded1">
            <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" >
                <EasingThicknessKeyFrame KeyTime="0" Value="20,0,0,0"/>
                <EasingThicknessKeyFrame KeyTime="0:0:0.4" Value="5"/>
            </ThicknessAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" >
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="55"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" >
                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="OnUnloaded1" >
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)">
                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="55"/>
                <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ScrollViewer VerticalScrollBarVisibility="Auto"  Grid.Row="0">
            <ItemsControl ItemsSource="{Binding Controls}" x:Name="x">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel HorizontalAlignment="Center"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </ScrollViewer>
        <Button Grid.Row="1" Content="Add" Margin="5" HorizontalAlignment="Left" Padding="15,2,15,2" Click="btAdd_OnClick"/>
        <Button Grid.Row="1" Content="Remove" Margin="64,5,0,5" HorizontalAlignment="Left" Padding="15,2,15,2" Click="btRemove_OnClick"/>
    </Grid>
</Window>

背后的代码

public class MyObservableCollection : ObservableCollection<FrameworkElement>
    {

        private Storyboard unloadedStoryboard;

        public Storyboard UnloadedSotryBoard
        {
            get { return unloadedStoryboard; }
            set
            {
                unloadedStoryboard = value;
                unloadedStoryboard.Completed += UnloadedStoryboardOnCompleted;
            }
        }

        public Storyboard LoadedSotryBoard { get; set; }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (FrameworkElement item in e.NewItems)
                    item.BeginStoryboard(LoadedSotryBoard);
            }
            base.OnCollectionChanged(e);
        }

        private HashSet<int> indexesToRemove = new HashSet<int>();

        protected override void RemoveItem(int index)
        {
            indexesToRemove.Add(index);
            var item = Items[index];
            UnloadedSotryBoard.Begin(item);
        }

        private void UnloadedStoryboardOnCompleted(object sender, EventArgs eventArgs)
        {
            foreach (var i in new HashSet<int>(indexesToRemove))
            {
                base.RemoveItem(i);
                indexesToRemove.Remove(i);
            }
        }

    }

    public partial class MainWindow
    {

        public MyObservableCollection Controls { get; set; }


        #region Constructors

        public MainWindow()
        {
            Controls = new MyObservableCollection();
            InitializeComponent();
            Controls.LoadedSotryBoard = (Storyboard) FindResource("OnLoaded1");
            Controls.UnloadedSotryBoard = (Storyboard) FindResource("OnUnloaded1");
        }

        #endregion


        #region Events

        private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
        {
            Controls.Add(new MyControl {DataContext = "A"});
            Controls.Add(new MyControl {DataContext = "B"});
            Controls.Add(new MyControl {DataContext = "C"});
        }

        private void btAdd_OnClick(object sender, RoutedEventArgs e)
        {
            Controls.Add(new MyControl {DataContext = (char) new Random().Next(0, Byte.MaxValue)});
        }

        private void btRemove_OnClick(object sender, RoutedEventArgs e)
        {
            if (Controls.Count == 0)
                return;
            Controls.RemoveAt(Controls.Count - 1);
        }

        #endregion
    }