Почему эта анимация не работает?

#wpf #xaml #animation

#wpf #xaml #Анимация

Вопрос:

Я изучаю анимацию WPF и создал простое демонстрационное приложение с довольно понятной анимацией. Я разделил основную сетку на три строки; строка кнопок вверху и две строки содержимого для остальной части экрана, одна красная и одна синяя. Полный XAML приведен ниже.

Есть две кнопки, показать красную и показать синюю. При нажатии каждой кнопки я хочу, чтобы область под рядом кнопок меняла цвет при медленном перемещении сверху вниз. Раскадровка устанавливает высоту обеих строк равной 0, затем анимирует нужную строку высотой 1 *, вот так:

 <Storyboard>
    <Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
    <Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
    <Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
  

Цвета меняются, как и ожидалось, но анимации нет. Итак, мой вопрос прост: почему анимация не работает?

Я использую пользовательский класс анимации, GridLengthAnimation (адаптированный из этой статьи CodeProject) для анимации длин сетки. Я воспроизвел приведенный ниже класс.

Чтобы воссоздать демонстрационный проект: Чтобы воссоздать мой демонстрационный проект, создайте новый проект WPF (я использовал VS 2010) и замените XAML в MainWindow.xaml на следующий:

 <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Utility="clr-namespace:Utility" Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Name="Buttons" Height="35" />
            <RowDefinition Name="RedRow" Height="0.5*" />
            <RowDefinition Name="BlueRow" Height="0.5*" />
        </Grid.RowDefinitions>

        <!-- Buttons -->
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Content="Show Red" Width="100" Margin="5" >
                <Button.Triggers>
                    <EventTrigger RoutedEvent="Button.Click">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
                                    <Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
                                    <Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Button.Triggers>
            </Button>
            <Button Content="Show Blue" Width="100" Margin="5" >
                <Button.Triggers>
                    <EventTrigger RoutedEvent="Button.Click">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
                                    <Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
                                    <Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Button.Triggers>
            </Button>
        </StackPanel>

        <!-- Grid Fills-->
        <Border Grid.Row="1" Background="Red" />
        <Border Grid.Row="2" Background="Blue" />

    </Grid>
</Window>
  

В MainWindow.xaml не добавлен дополнительный код.

Добавьте класс C # в проект с именем GridLengthAnimation.cs . Замените код в этом классе следующим:

 using System;
using System.Windows.Media.Animation;
using System.Windows;

namespace Utility
{
    /// <summary>
    /// Enables animation of WPF Grid row heights and column widths.
    /// </summary>
    /// <remarks>Adapted from Graus amp; Sivakumar, "WPF Tutorial - Part 2 : Writing a custom animation class",
    /// http://www.codeproject.com/KB/WPF/GridLengthAnimation.aspx, retrieved 08/12/2010.</remarks>
    internal class GridLengthAnimation : AnimationTimeline
    {
        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength), 
                typeof(GridLengthAnimation));
        }

        public override Type TargetPropertyType
        {
            get 
            {
                return typeof(GridLength);
            }
        }

        protected override Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public static readonly DependencyProperty FromProperty;
        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(FromProperty);
            }
            set
            {
                SetValue(FromProperty, value);
            }
        }

        public static readonly DependencyProperty ToProperty;
        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(ToProperty);
            }
            set
            {
                SetValue(ToProperty, value);
            }
        }

        public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(FromProperty)).Value;
            double toVal = ((GridLength)GetValue(ToProperty)).Value;

            if (animationClock.CurrentProgress != null)
            {
                if (fromVal > toVal) 
                {
                    return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal)   toVal, GridUnitType.Star);
                }
                else 
                {
                    return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal)   fromVal, GridUnitType.Star);
                }
            }
            else
            {
                return null;
            }
        }
    }
}
  

Ответ №1:

Я нашел свой ответ в этом сообщении в блоге. Оказывается, существует проблема с анимацией свойств высоты или ширины. Я решил проблему, используя эффект растворения вместо стирания. Чтобы анимировать растворение, объявите оба элемента управления в одной строке и столбце сетки, что загрузит их друг на друга. Объявите элемент управления по умолчанию последним, что сделает его видимым элементом управления. Затем измените значение непрозрачности элемента управления по умолчанию на ноль, чтобы скрыть его, и обратно на 1, чтобы показать его.

Если анимируемые элементы управления являются UserControls или другими элементами управления, на которые вам нужно нажать, вам нужно сделать еще один шаг. Это потому, что установка непрозрачности элемента управления на ноль просто делает его невидимым. Это по-прежнему будет препятствовать щелчку по элементу управления под ним. Итак, объявите Рендеринг.Преобразуйте элемент управления по умолчанию, затем анимируйте свойство scaleY, чтобы установить для него значение 0 при невидимости и 1 при отображении.

Вот пример из производственного приложения, над которым я работаю. Он переключается между списком заметок и календарем (два разных пользовательских элемента управления) на панели Навигатора интерфейса в стиле проводника. Вот объявление двух элементов управления:

 <!-- ClientArea: Navigator -->
<Grid x:Name="Navigator">
    <View:CalendarNavigator x:Name="Calendar"  />
    <View:NoteListNavigator x:Name="NoteList">
        <View:NoteListNavigator.RenderTransform>
            <ScaleTransform ScaleX="1" ScaleY="1" />
        </View:NoteListNavigator.RenderTransform>
    </View:NoteListNavigator>
</Grid>
  

Обратите внимание на объявление ScaleTransform в списке примечаний. Я использую пару кнопок ленты для переключения между двумя пользовательскими элементами управления:

 <ribbon:RibbonToggleButton x:Name="NoteListViewButton" LargeImageSource="..ImagesListViewLarge.png" SmallImageSource="..ImagesListViewSmall.png" Label="Note List" Click="OnViewButtonClick">
    <ribbon:RibbonToggleButton.Triggers>
        <EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="1" Duration="0:0:0" />
                        <DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
        </ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
                    
<ribbon:RibbonToggleButton x:Name="CalendarViewButton" LargeImageSource="..ImagesCalendarViewLarge.png" SmallImageSource="..ImagesCalendarViewSmall.png" Label="Calendar" Click="OnViewButtonClick">
    <ribbon:RibbonToggleButton.Triggers>
        <EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1" />
                        <DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="0" Duration="0:0:0" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
  

Масштабные преобразования убирают невидимый список заметок при отображении календаря, чтобы я мог щелкнуть по элементам управления моим календарем. Обратите внимание, что мне нужны были полные ссылки на свойства scaleY в моих раскадровках. Вот почему ссылки заключены в круглые скобки.

Надеюсь, это поможет кому-то еще в будущем! Скорее всего, это буду я, поскольку я, вероятно, забуду, как я это сделал…