Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Ao trabalhar com animações no WPF, há uma série de dicas e truques que podem melhorar o desempenho de suas animações e evitar sua frustração.
Questões Gerais
Animar a posição de uma barra de rolagem ou controle deslizante congela-a
Se animares a posição de uma barra de rolagem ou de um controle deslizante usando uma animação que tenha um FillBehavior de HoldEnd (o valor padrão), o utilizador não poderá mais mover a barra de rolagem ou o controle deslizante. Isso acontece porque, ainda que a animação tenha terminado, ela continua a substituir o valor base da propriedade de destino. Para impedir que a animação substitua o valor atual da propriedade, remova-a ou atribua-lhe um FillBehavior de Stop. Para obter mais informações e um exemplo, consulte Definir uma propriedade após animá-la com um Storyboard.
Animar a saída de uma animação não tem efeito
Não é possível animar um objeto que seja a saída de outra animação. Por exemplo, se você usar um ObjectAnimationUsingKeyFrames para animar o Fill de a Rectangle de a RadialGradientBrush para um SolidColorBrush, não poderá animar nenhuma propriedade do RadialGradientBrush ou SolidColorBrush.
Não é possível alterar o valor de uma propriedade depois de animá-la
Em alguns casos, pode parecer que não é possível alterar o valor de uma propriedade depois que ela é animada, mesmo depois que a animação termina. Isso porque, mesmo que a animação tenha terminado, ela ainda está sobrepondo-se ao valor base da propriedade. Para impedir que a animação substitua o valor atual da propriedade, remova-a ou atribua-lhe um FillBehavior de Stop. Para obter mais informações e um exemplo, consulte Definir uma propriedade após animá-la com um Storyboard.
Alterar uma linha do tempo não tem efeito
Embora a maioria das Timeline propriedades sejam animáveis e possam ser vinculadas a dados, alterar os valores de propriedade de um ativo Timeline parece não ter efeito. Isso porque, quando um Timeline é iniciado, o sistema de cronometragem faz uma cópia do Timeline e o usa para criar um Clock objeto. A modificação do original não tem efeito na cópia do sistema.
Para que um Timeline reflita as alterações, seu relógio deve ser regenerado e usado para substituir o relógio criado anteriormente. Os relógios não são regenerados automaticamente para si. A seguir estão várias maneiras de aplicar alterações na linha do tempo:
Se a linha do tempo for ou pertencer a um Storyboard, você poderá fazê-lo refletir as alterações reaplicando seu storyboard usando um BeginStoryboard ou o Begin método. Isso tem o efeito colateral de também reiniciar a animação. No código, você pode usar o Seek método para avançar o storyboard de volta à sua posição anterior.
Se você aplicou uma animação diretamente a uma propriedade usando o BeginAnimation método, chame o BeginAnimation método novamente e passe-lhe a animação que foi modificada.
Se você estiver trabalhando diretamente no nível do relógio, crie e aplique um novo conjunto de relógios e use-os para substituir o conjunto anterior de relógios gerados.
Para obter mais informações sobre cronogramas e relógios, consulte Visão geral do sistema de animação e cronometragem.
FillBehavior.Stop não funciona como esperado
Há momentos em que definir a FillBehavior propriedade como Stop parece não ter efeito, como quando uma animação "passa" para outra porque tem uma HandoffBehavior configuração de SnapshotAndReplace.
O exemplo a seguir cria um Canvas, a Rectangle e um TranslateTransform. O TranslateTransform será animado para mover o Rectangle ao redor do Canvas.
<Canvas Width="600" Height="200">
<Rectangle
Canvas.Top="50" Canvas.Left="0"
Width="50" Height="50" Fill="Red">
<Rectangle.RenderTransform>
<TranslateTransform
x:Name="MyTranslateTransform"
X="0" Y="0" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
Os exemplos nesta seção usam os objetos anteriores para demonstrar vários casos em que a FillBehavior propriedade não se comporta como você poderia esperar.
FillBehavior="Stop" e HandoffBehavior com várias animações
Às vezes, parece que uma animação ignora sua FillBehavior propriedade quando é substituída por uma segunda animação. Tomemos o exemplo a seguir, que cria dois Storyboard objetos e os usa para animar o mesmo TranslateTransform mostrado no exemplo anterior.
O primeiro Storyboard, B1
, anima a propriedade X do TranslateTransform de 0 a 350, movendo o retângulo 350 pixels para a direita. Quando a animação atinge o final de sua duração e para de reproduzir, a X propriedade reverte para seu valor original, 0. Como resultado, o retângulo move-se 350 pixels para a direita e, em seguida, salta novamente para a sua posição original.
<Button Content="Start Storyboard B1">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard x:Name="B1">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
From="0" To="350" Duration="0:0:5"
FillBehavior="Stop"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
O segundo Storyboard, B2
, também anima a propriedade X do mesmo TranslateTransform. Como apenas a To propriedade da animação neste Storyboard é definida, a animação usa o valor atual da propriedade que anima como seu valor inicial.
<!-- Animates the same object and property as the preceding
Storyboard. -->
<Button Content="Start Storyboard B2">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard x:Name="B2">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
To="500" Duration="0:0:5"
FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
Se você clicar no segundo botão enquanto o primeiro Storyboard está sendo reproduzido, você pode esperar o seguinte comportamento:
O primeiro storyboard termina e envia o retângulo de volta à sua posição original, porque a animação tem um FillBehavior de Stop.
O segundo storyboard é ativado e anima a partir da posição atual, começando em 0 até 500.
Mas não é isso que acontece. Em vez disso, o retângulo não salta para trás; continua a mover-se para a direita. Isso ocorre porque a segunda animação usa o valor atual da primeira animação como seu valor inicial e anima desse valor para 500. Quando a segunda animação substitui a primeira porque a SnapshotAndReplaceHandoffBehavior é usada, a FillBehavior da primeira animação não importa.
FillBehavior e o Evento Concluído
Os exemplos seguintes demonstram outro cenário em que o StopFillBehavior parece não surtir efeito. Novamente, o exemplo usa um Storyboard para animar a X propriedade do TranslateTransform de 0 a 350. No entanto, desta vez, o exemplo regista-se para o evento Completed.
<Button Content="Start Storyboard C">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard Completed="StoryboardC_Completed">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
From="0" To="350" Duration="0:0:5"
FillBehavior="Stop" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
O Completed manipulador de eventos inicia outro Storyboard que anima a mesma propriedade de seu valor atual para 500.
private void StoryboardC_Completed(object sender, EventArgs e)
{
Storyboard translationAnimationStoryboard =
(Storyboard)this.Resources["TranslationAnimationStoryboardResource"];
translationAnimationStoryboard.Begin(this);
}
Private Sub StoryboardC_Completed(ByVal sender As Object, ByVal e As EventArgs)
Dim translationAnimationStoryboard As Storyboard = CType(Me.Resources("TranslationAnimationStoryboardResource"), Storyboard)
translationAnimationStoryboard.Begin(Me)
End Sub
A seguir está a marcação que define o segundo Storyboard como um recurso.
<Page.Resources>
<Storyboard x:Key="TranslationAnimationStoryboardResource">
<DoubleAnimation
Storyboard.TargetName="MyTranslateTransform"
Storyboard.TargetProperty="X"
To="500" Duration="0:0:5" />
</Storyboard>
</Page.Resources>
Ao executar o Storyboard, pode esperar que a propriedade X do TranslateTransform seja animada de 0 a 350, reverter para 0 após a conclusão (já que tem uma configuração FillBehavior de Stop) e, em seguida, seja animada de 0 a 500. Em vez disso, o TranslateTransform move-se de 0 para 350 e depois para 500.
Isso ocorre devido à ordem na qual o WPF gera eventos e porque os valores de propriedade são armazenados em cache e não são recalculados, a menos que a propriedade seja invalidada. O Completed evento é processado primeiro porque foi acionado pela linha do tempo raiz (a primeira Storyboard). Neste momento, a X propriedade ainda retorna seu valor animado porque ainda não foi invalidada. O segundo Storyboard usa o valor armazenado em cache como seu valor inicial e começa a animação.
Desempenho
As animações continuam a ser executadas depois de navegar para fora de uma página
Quando navega para longe de um Page que contém animações em execução, essas animações continuarão a ser executadas até que o Page seja coletado pelo coletor de lixo. Dependendo do sistema de navegação que está a utilizar, uma página de que se afastou pode permanecer na memória por um período indeterminado, ao consumir recursos com as suas animações. Isso é mais percetível quando uma página contém animações ("ambiente") em execução constante.
Por esse motivo, é uma boa ideia usar o Unloaded evento para remover animações quando você navega para fora de uma página.
Há diferentes maneiras de remover uma animação. As técnicas a seguir podem ser usadas para remover animações que pertencem a um Storyboardarquivo .
Para remover um Storyboard que você iniciou com um acionador de evento, consulte Como remover um Storyboard.
Para usar o código para remover um Storyboard, consulte o Remove método.
A próxima técnica pode ser usada independentemente de como a animação foi iniciada.
- Para remover animações de uma propriedade específica, use o BeginAnimation(DependencyProperty, AnimationTimeline) método. Especifique a propriedade que está sendo animada como o primeiro parâmetro e
null
como o segundo. Isso removerá todos os relógios de animação da propriedade.
Para obter mais informações sobre as diferentes maneiras de animar propriedades, consulte Visão geral das técnicas de animação de propriedade.
Usar o Compose HandoffBehavior consome recursos do sistema
Quando você aplica um Storyboard, AnimationTimeline, ou AnimationClock a uma propriedade usando o ComposeHandoffBehavior, quaisquer Clock objetos anteriormente associados a essa propriedade continuam a consumir recursos do sistema, o sistema de temporização não removerá esses relógios automaticamente.
Para evitar problemas de desempenho ao aplicar um grande número de relógios usando Composeo , você deve remover os relógios de composição da propriedade animada depois que eles forem concluídos. Existem várias maneiras de remover um relógio.
Para remover todos os relógios de uma propriedade, use o método ApplyAnimationClock(DependencyProperty, AnimationClock) ou BeginAnimation(DependencyProperty, AnimationTimeline) do objeto animado. Especifique a propriedade que está sendo animada como o primeiro parâmetro e
null
como o segundo. Isso removerá todos os relógios de animação da propriedade.Para remover um específico AnimationClock de uma lista de relógios, use a Controller propriedade do AnimationClock para recuperar um ClockControllere, em seguida, chame o Remove método do ClockController. Isso geralmente é feito no manipulador de eventos de um Completed relógio. Note que apenas relógios raiz podem ser controlados por um ClockController; a propriedade Controller de um relógio filho retornará
null
. Observe também que o Completed evento não será chamado se a duração efetiva do temporizador for para sempre. Nesse caso, o usuário precisará determinar quando chamar Remove.
Isso é principalmente um problema para animações em objetos que têm uma longa vida útil. Quando um objeto é recolhido pelo coletor de lixo, os seus relógios também serão desconectados e recolhidos pelo coletor de lixo.
Para obter mais informações sobre objetos de relógio, consulte Visão geral do sistema de animação e cronometragem.
Ver também
.NET Desktop feedback