Проблема с анимацией раскадровки WPF при использовании VisualBrush

Я играл с раскадровками, анимацией переворачивания и визуальными кистями. Однако я столкнулся с проблемой. Ниже приведены xaml и код программной части небольшого образца, который я быстро собрал, чтобы попытаться продемонстрировать проблему.

При первом запуске приложения вы видите красный квадрат и две кнопки. Если вы нажмете кнопку «Перевернуть», красный квадрат «перевернется» и появится синий. На самом деле все, что происходит, - это то, что масштаб ширины StackPanel, в котором находится красный квадрат, уменьшается до тех пор, пока не достигнет нуля, а затем StackPanel, где находится синий квадрат, ширина которого изначально масштабирована до нуля, имеет свой ширина увеличилась. Если вы нажмете кнопку «Перевернуть» несколько раз, анимация будет выглядеть нормально и плавно.

Теперь, если вы нажмете кнопку «Отражение», отражение красных / синих кнопок будет добавлено к их соответствующим StackPanels. Нажатие кнопки «Перевернуть» теперь по-прежнему вызывает анимацию переворота, но это уже не плавная анимация. Ширина StackPanels часто не уменьшается до нуля. Ширина немного уменьшается, но затем просто останавливается, прежде чем стать полностью невидимой. Затем, как обычно, появляется другая StackPanel. Единственное, что изменилось, - это добавление отражения, которое представляет собой просто VisualBrush.

Ниже приведен код. Кто-нибудь знает, почему анимации различаются между двумя случаями (остановка во втором случае)?

Спасибо.

<Window
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xml:lang="en-US"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
 x:Class="WpfFlipTest.Window1"
 x:Name="Window"
 Title="Window1"
 Width="214" Height="224">
  <Window.Resources>
    <Storyboard x:Key="sbFlip">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="redStack"  Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="0"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetName="blueStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.8" Value="1"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="sbFlipBack">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="blueStack"  Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="0"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetName="redStack" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.8" Value="1"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
  </Window.Resources>
  <Grid x:Name="LayoutRoot" Background="Gray">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <StackPanel Name="redStack" Grid.Row="0" Grid.Column="0" RenderTransformOrigin="0.5,0.5">
      <StackPanel.RenderTransform>
        <ScaleTransform/>
      </StackPanel.RenderTransform>
      <Border Name="redBorder" BorderBrush="Transparent" BorderThickness="4" Width="Auto" Height="Auto">
        <Button Margin="0" Name="redButton" Height="75" Background="Red" Width="105" />
      </Border>
      <Border Width="{Binding ElementName=redBorder, Path=ActualWidth}" 
              Height="{Binding ElementName=redBorder, Path=ActualHeight}" 
              Opacity="0.2" BorderBrush="Transparent" BorderThickness="4" Name="redRefelction" Visibility="Collapsed">
        <Border.OpacityMask>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <LinearGradientBrush.GradientStops>
              <GradientStop Offset="0" Color="Black"/>
              <GradientStop Offset=".6" Color="Transparent"/>
            </LinearGradientBrush.GradientStops>
          </LinearGradientBrush>
        </Border.OpacityMask>
        <Border.Background>
          <VisualBrush Visual="{Binding ElementName=redButton}">
            <VisualBrush.Transform>
              <ScaleTransform ScaleX="1" ScaleY="-1" 
                CenterX="52.5" 
                CenterY="37.5" />
            </VisualBrush.Transform>
          </VisualBrush>
        </Border.Background>
      </Border>
    </StackPanel>
    <StackPanel Name="blueStack" Grid.Row="0" Grid.Column="0" RenderTransformOrigin="0.5,0.5">
      <StackPanel.RenderTransform>
        <ScaleTransform ScaleX="0"/>
      </StackPanel.RenderTransform>
      <Border Name="blueBorder" BorderBrush="Transparent" BorderThickness="4" Width="Auto" Height="Auto">
        <Button Grid.Row="0" Grid.Column="1" Margin="0" Width="105" Background="Blue" Name="blueButton" Height="75"/>
      </Border>
      <Border Width="{Binding ElementName=blueBorder, Path=ActualWidth}" 
              Height="{Binding ElementName=blueBorder, Path=ActualHeight}" 
              Opacity="0.2" BorderBrush="Transparent" BorderThickness="4" Name="blueRefelction" Visibility="Collapsed">
        <Border.OpacityMask>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <LinearGradientBrush.GradientStops>
              <GradientStop Offset="0" Color="Black"/>
              <GradientStop Offset=".6" Color="Transparent"/>
            </LinearGradientBrush.GradientStops>
          </LinearGradientBrush>
        </Border.OpacityMask>
        <Border.Background>
          <VisualBrush Visual="{Binding ElementName=blueButton}">
            <VisualBrush.Transform>
              <ScaleTransform ScaleX="1" ScaleY="-1" 
                CenterX="52.5" 
                CenterY="37.5" />
            </VisualBrush.Transform>
          </VisualBrush>
        </Border.Background>
      </Border>
    </StackPanel>
    <Button Grid.Row="1" Click="FlipButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Flip</Button>
    <Button Grid.Row="0" Grid.Column="1" Click="ReflectionButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Reflection</Button>
  </Grid>
</Window>

Вот обработчики нажатия кнопки:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace WpfFlipTest
{
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
    }

    bool flipped = false;
    private void FlipButton_Click(object sender, RoutedEventArgs e)
    {
      Storyboard sbFlip = (Storyboard)Resources["sbFlip"];
      Storyboard sbFlipBack = (Storyboard)Resources["sbFlipBack"];

      if (flipped)
      {
        sbFlipBack.Begin();
        flipped = false;
      }
      else
      {
        sbFlip.Begin();
        flipped = true;
      }
    }

    bool reflection = false;
    private void ReflectionButton_Click(object sender, RoutedEventArgs e)
    {
      if (reflection)
      {
        reflection = false;
        redRefelction.Visibility = Visibility.Collapsed;
        blueRefelction.Visibility = Visibility.Collapsed;
      }
      else
      {
        reflection = true;
        redRefelction.Visibility = Visibility.Visible;
        blueRefelction.Visibility = Visibility.Visible;
      }
    }
  }
}







ОБНОВЛЕНИЕ:

Я тестировал это еще раз, чтобы попытаться выяснить, что вызывает проблему, которую я вижу, и я считаю, что нашел то, что вызывает проблему.

Ниже я вставил новый xaml и код программной части. Новый образец, представленный ниже, очень похож на исходный образец с небольшими изменениями. Xaml в основном состоит из двух панелей стека, каждая из которых содержит две границы. Вторая граница на каждой панели стека - это визуальная кисть (отражение границы над ней). Теперь, когда я нажимаю кнопку «Перевернуть», у одной панели стека ScaleX уменьшается до нуля, а у второй панели стека, у которой начальный ScaleX равен нулю, увеличивается ScaleX до 1. Эта анимация дает иллюзию переворачивания. Также есть два текстовых блока, которые отображают масштабный коэффициент каждой панели стека. Я добавил их, чтобы попытаться диагностировать свою проблему.

Проблема в том (как описано в оригинальном посте), что анимация переворачивания не плавная. Каждый раз, когда я нажимаю кнопку переворота, анимация запускается, но всякий раз, когда коэффициент ScaleX достигает значений от 0,14 до 0,16, анимация выглядит так, как будто она останавливается, а на панелях стека ScaleX никогда не уменьшается до нуля, поэтому они никогда не исчезают полностью. Странно то, что если я изменю свойства ширины / высоты границ frontBorder и backBorder, определенные ниже, чтобы использовать явные значения вместо Auto, такие как Width = 105 и Height = 75 (чтобы соответствовать кнопке в граница) все работает нормально. Первые два или три раза анимация заикается, но после этого флип происходит плавно и безупречно. (Кстати, когда анимация запускается в первый раз, что-то происходит в фоновом режиме, какая-то инициализация, из-за которой в первый раз она немного замедляется?)

Возможно ли, что проблема с автоматической шириной / высотой границ? Я могу воспроизводить это каждый раз, но я не уверен, почему автоматическая ширина / высота может быть проблемой.

Ниже приведен образец. Спасибо за помощь.

<Window x:Class="FlipTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
  <Window.Resources>
    <Storyboard x:Key="sbFlip">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="front"  Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.5" Storyboard.TargetName="back" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
    <Storyboard x:Key="sbFlipBack">
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="back"  Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="0"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.5" Storyboard.TargetName="front" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
  </Window.Resources>
  <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <StackPanel x:Name="front" RenderTransformOrigin="0.5,0.5">
      <StackPanel.RenderTransform>
        <ScaleTransform/>
      </StackPanel.RenderTransform>
      <Border Name="frontBorder" BorderBrush="Yellow" BorderThickness="2" Width="Auto" Height="Auto">
        <Button Margin="0" Name="redButton" Height="75" Background="Red" Width="105" Click="FlipButton_Click"/>
      </Border>
      <Border Width="{Binding ElementName=frontBorder, Path=ActualWidth}" 
              Height="{Binding ElementName=frontBorder, Path=ActualHeight}" 
              Opacity="0.2" BorderBrush="Transparent">
        <Border.OpacityMask>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <LinearGradientBrush.GradientStops>
              <GradientStop Offset="0" Color="Black"/>
              <GradientStop Offset=".6" Color="Transparent"/>
            </LinearGradientBrush.GradientStops>
          </LinearGradientBrush>
        </Border.OpacityMask>
        <Border.Background>
          <VisualBrush Visual="{Binding ElementName=frontBorder}">
            <VisualBrush.Transform>
              <ScaleTransform ScaleX="1" ScaleY="-1" 
                CenterX="52.5" 
                CenterY="37.5" />
            </VisualBrush.Transform>
          </VisualBrush>
        </Border.Background>
      </Border>
    </StackPanel>
    <StackPanel x:Name="back" RenderTransformOrigin="0.5,0.5">
      <StackPanel.RenderTransform>
        <ScaleTransform ScaleX="0"/>
      </StackPanel.RenderTransform>
      <Border Name="backBorder" BorderBrush="Yellow" BorderThickness="2" Width="Auto" Height="Auto">
        <Button Margin="0" Width="105" Background="Blue" Name="blueButton" Height="75" Click="FlipButton_Click"/>
      </Border>
      <Border Width="{Binding ElementName=backBorder, Path=ActualWidth}" 
              Height="{Binding ElementName=backBorder, Path=ActualHeight}" 
              Opacity="0.2" BorderBrush="Transparent">
        <Border.OpacityMask>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <LinearGradientBrush.GradientStops>
              <GradientStop Offset="0" Color="Black"/>
              <GradientStop Offset=".6" Color="Transparent"/>
            </LinearGradientBrush.GradientStops>
          </LinearGradientBrush>
        </Border.OpacityMask>
        <Border.Background>
          <VisualBrush Visual="{Binding ElementName=backBorder}">
            <VisualBrush.Transform>
              <ScaleTransform ScaleX="1" ScaleY="-1" 
                CenterX="52.5" 
                CenterY="37.5" />
            </VisualBrush.Transform>
          </VisualBrush>
        </Border.Background>
      </Border>
    </StackPanel>
    <Button Grid.Row="1" Click="FlipButton_Click" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76">Flip</Button>
    <TextBlock Grid.Row="2" Grid.Column="0" Foreground="DarkRed" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Text="{Binding ElementName=front, Path=(UIElement.RenderTransform).(ScaleTransform.ScaleX)}"/>
    <TextBlock Grid.Row="3" Grid.Column="0" Foreground="DarkBlue" Height="19.45" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Text="{Binding ElementName=back, Path=(UIElement.RenderTransform).(ScaleTransform.ScaleX)}"/>
  </Grid>
</Window>

Код программной части:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace FlipTest
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
    }

    bool flipped = false;
    private void FlipButton_Click(object sender, RoutedEventArgs e)
    {
      Storyboard sbFlip = (Storyboard)Resources["sbFlip"];
      Storyboard sbFlipBack = (Storyboard)Resources["sbFlipBack"];

      if (flipped)
      {
        sbFlipBack.Begin();
        flipped = false;
      }
      else
      {
        sbFlip.Begin();
        flipped = true;
      }
    }
  }
}

person Flack    schedule 21.01.2010    source источник
comment
Я запускал это пару раз, и он работает, как и следовало ожидать. Анимация плавная даже при включенном отражении - у вас 386? :)   -  person PaulB    schedule 27.01.2010


Ответы (1)


Я попробовал это еще раз на своей новой машине x64, и анимация прошла гладко, без каких-либо проблем. То, что я видел раньше, должно быть связано с плохой производительностью на моей старой машине.

person Flack    schedule 27.05.2010