WPF- Animações e Relógio Animado


Neste artigo veremos alguns recursos gráficos e de animação do WPF aplicados na prática incluindo ai a criação de um relógio animado.

A Windows Presentation Foundation (WPF) fornece um poderoso conjunto de gráficos e recursos de layout que permitem criar interfaces de usuário atrativas e documentos atraentes. A animação é um recurso que pode fazer uma interface de usuário atraente ainda mais espetacular e utilizável. Usando a animação podemos animar uma cor de fundo ou aplicar um transição animada, você pode criar transições de tela dramáticas ou fornecer úteis indicações visuais.

Se você entender alguns conceitos importantes sobre o sistema de temporização, as animações WPF pode ser mais fácil de usar. O mais importante é que, em WPF, você anima objetos através da aplicação de animação para as suas propriedades individuais.

Por exemplo, para fazer um elemento aumentar, você deve animar as suas propriedades Width e Height. Para fazer um objeto desaparecer da vista, você deve animar a sua propriedade Opacity e por ai vai...

Para uma propriedade possuir capacidades de animação, ela deve atender os seguintes requisitos:

  1. Deve ser uma propriedade de dependência.(dependency property);
  2. Deve pertencer a uma classe que herda de DependencyObject e implementa a interface IAnimatable;
  3. Deve haver um tipo de animação compatível disponível. (Se a WPF não forneçer um, você pode criar o seu.);

A WPF contém muitos objetos que possuem propriedades IAnimatable. Controles como Button e TabControl e também objetos Panel e Shape herdam de DependencyObject. A maioria de suas propriedades são propriedades de dependência.

Você pode usar animações em praticamente qualquer lugar, as quais incluem em estilos e modelos de controle. As animações não precisam necessariamente serem visuais, você pode animar objetos que não fazem parte da interface do usuário.

Para começar vamos criar uma animação bem simples que consiste em fazer um elemento aparecer e desaparecer.

Abra o Visual Basic 2010 Express Edition e crie um novo projeto do tipo WPF Application com o nome EfeitoVisual:

Este exemplo mostra como usar uma animação WPF para animar o valor de uma propriedade de dependência. Ele usa um DoubleAnimation, que é um tipo de animação que gera valores Double, para animar a propriedade Opacity de um Rectangle. Como resultado criaremos o efeito visual no qual o retângulo irá desaparecer da visão.

Vamos começar criando o elemento Rectangle em um StackPanel usando código XAML e depois criar uma animação e aplicá-la a propriedade Opacity do retângulo.

Abaixo vemos o código XAML do arquivo MainWindow.xaml e o resultado visual que mostra a criação do retângulo:

Uma maneira de fazer um elemento aparecer e desaparecer da vista é animar a sua propriedade Opacity. (eu já falei isso lembra ?)

Como a propriedade Opacity é do tipo Double, você precisa de uma animação que produza valores doubles.

Um DoubleAnimation é o recurso que precisamos usar para tal animação.

Um DoubleAnimation cria uma transição entre dois valores double. Para especificar o seu valor inicial, você define sua propriedade From e para especificar o valor final, você define a sua propriedade To. (óbvio...)

Um valor de opacidade de 1,0 torna o objeto completamente opaco, e um valor de opacidade de 0,0 o torna completamente invisível. Para fazer a transição de animação 1,0 a 0,0 você define sua propriedade From para 1,0 e de sua propriedade To a 0.0. A seguir temos o código XAMl que cria um DoubleAnimation com esses valores:

<DoubleAnimation From="1.0" To="0.0" />

Após isso temos que especificar uma duração. A duração de uma animação especifica quanto tempo leva para ir do seu valor inicial para o seu valor de final. O código abaixo mostra como definir uma duração de cinco segundos em XAML para o nosso exemplo:

<DoubleAnimation From="1.0" To="0.0" Duration="0:0:5" />

No código anterior fizemos uma animação onde aplicamos transições de 1,0 a 0,0, o que faz com que o elemento de destino sofra um efeito visual que o torna opaco e depois o torna completamente invisível.

Para fazer o elemento reaparecer depois que ele desaparece defina a propriedade AutoReverse da animação como True. Para fazer com que a animação se repita indefinidamente, defina sua propriedade RepeatBehavior para Forever. O código XAML a seguir mostra exatamente isso:

<DoubleAnimation From="1.0" To="0.0" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever"/>

Para aplicar uma animação a um objeto, você cria um Storyboard e usa as propriedades anexadas TargetName e TargetProperty para especificar o objeto e a propriedade a animar.

O Storyboard tem que saber onde aplicar a animação e para isso ela usa a propriedade anexada Storyboard.TargetName para especificar o objeto a animar.O código a seguir mostra como definir o nome do alvo da DoubleAnimation para MacorattiRetangulo em XAML.


<Storyboard>
    <DoubleAnimation
        Storyboard.TargetName="MacorattiRetangulo" 
        Storyboard.TargetProperty="Opacity"
        From="1.0" To="0.0" Duration="0:0:5" 
        AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>

Observe como usamos a propriedade anexada TargetProperty para especificar a propriedade a animar. Assim configuramos a animação para a propriedade Opacity do retângulo no XAML.

A maneira mais fácil de aplicar e iniciar um storyboard em XAML é usar um trigger ou seja disparador de evento.

Vamos então criar um objeto BeginStoryboard e associar o storyboard a ele. Um BeginStoryboard é um tipo de TriggerAction que se aplica e inicia um Storyboard.

<BeginStoryboard>
  <Storyboard>
    <DoubleAnimation
      Storyboard.TargetName="MacorattiRetangulo" 
      Storyboard.TargetProperty="Opacity"
      From="1.0" To="0.0" Duration="0:0:5" 
      AutoReverse="True" RepeatBehavior="Forever" />
  </Storyboard>
</BeginStoryboard>

A seguir crie um EventTrigger e adicione o BeginStoryboard à sua coleção de Ações. Defina a propriedade RoutedEvent do EventTrigger para o evento roteado que você deseja iniciar o Storyboard e para completar adicione o EventTrigger a coleção Triggers do Retângulo:

<Grid>
        <StackPanel Margin="10">
            <Rectangle
        Name="MacorattiRetangulo"
        Width="100" 
        Height="100"
        Fill="Red">
                <Rectangle.Triggers>
                    <!-- Animação do retangulo. -->
                    <EventTrigger RoutedEvent="Rectangle.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="MacorattiRetangulo" 
                                    Storyboard.TargetProperty="Opacity"
                                    From="1.0" To="0.0" Duration="0:0:5" 
                                    AutoReverse="True" RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Rectangle.Triggers>
            </Rectangle>
        </StackPanel>
    </Grid>

Executando o projeto iremos visualizar o retângulo vermelho na janela que irá desaparecer e reaparecer indefinidamente:

Criando o Relógio Animado

Vamos agora aplicar os mesmos conceitos para criar algo mais consistente: um relógio animado.

Neste exemplo estamos definindo as propriedades de animação via código mostrando portanto essa possibilidade.

Abra o Visual Basic 2010 Express Edition e crie um novo projeto do tipo WPF Application com o nome RelogioAnimado:

A seguir abra o arquivo MainWindow.xaml copie o código XAML abaixo neste arquivo:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Relogio" Height="817.388" Width="1000" WindowStyle="ThreeDBorderWindow" Foreground="BurlyWood" Topmost="False" WindowStartupLocation="CenterScreen" WindowState="Normal">
    <Grid ShowGridLines="True" Background="SteelBlue">
        <Border Margin="34.296,32.867,54.302,11.432" Name="Border1" Background="LightSteelBlue">
            <Grid Height="425.842" Name="Grid1" Width="770.231">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="95.051*" />
                    <ColumnDefinition Width="675.18*" />
                </Grid.ColumnDefinitions>
                <Ellipse Grid.Column="1" Margin="20.006,-12.861,119.174,-97.172" Name="Ellipse1" Stroke="Black" Fill="Snow" />
                <Rectangle Fill="Brown" Height="4" Margin="287.229,0,171.48,161.764" Name="Rectangle2" Stroke="Black" VerticalAlignment="Bottom" Grid.Column="1" />
                <Rectangle Fill="Black" Height="5" Margin="287.229,0,201.489,164.335" Name="Rectangle1" Stroke="Black" VerticalAlignment="Bottom" Grid.Column="1" />
                <Label Grid.Column="1" Height="48.586" Margin="261.507,0,0,0" Name="Label1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="48.586"
 Foreground="BlueViolet" FontSize="28">
                    <Label.BitmapEffect>
                        <EmbossBitmapEffect />
                    </Label.BitmapEffect> 12
               </Label>
                <Label FontSize="28" Foreground="BlueViolet" Height="48.586" Margin="37.154,0,0,144.329" Name="Label2" VerticalAlignment="Bottom" Grid.Column="1" 
HorizontalAlignment="Left" Width="48.586">
                    <Label.BitmapEffect>
                        <EmbossBitmapEffect />
                    </Label.BitmapEffect> 9
                </Label>
                <Label FontSize="28" Foreground="BlueViolet" Height="48.586" Margin="261.507,0,0,-97.172" Name="Label3" VerticalAlignment="Bottom" Grid.Column="1" 
HorizontalAlignment="Left" Width="48.586">
                    <Label.BitmapEffect>
                        <EmbossBitmapEffect />
                    </Label.BitmapEffect> 6
                </Label>
                <Label FontSize="28" Foreground="BlueViolet" Height="48.586" Margin="0,0,119.174,144.329" Name="Label4" VerticalAlignment="Bottom" Grid.Column="1" 
HorizontalAlignment="Right" Width="48.586">
                    <Label.BitmapEffect>
                        <EmbossBitmapEffect />
                    </Label.BitmapEffect> 3
                </Label>
                <Rectangle Fill="Yellow" Height="6.429" Margin="287.229,0,248.646,162.906" Name="Rectangle3" Stroke="Black" VerticalAlignment="Bottom" Grid.Column="1" />
                <Ellipse Fill="Black" Height="47.157" Margin="264.365,0,0,144.329" Name="Ellipse2" Stroke="Black" VerticalAlignment="Bottom" Grid.Column="1" HorizontalAlignment="Left" Width="45.728" />
            </Grid>
        </Border>
    </Grid>
</Window>

Você deverá visualizar a seguinte imagem:

Agora abra o arquivo MainWindow.xaml.vb e inclua o código abaixo:

Class MainWindow 

    Private Sub Minit()
        Dim da As New Animation.DoubleAnimation()
        da.From = 0
        da.[To] = 360
        da.Duration = New Duration(TimeSpan.FromSeconds(3600))
        da.RepeatBehavior = Animation.RepeatBehavior.Forever
        Dim rt As New RotateTransform()
        Rectangle1.RenderTransform = rt
        rt.BeginAnimation(RotateTransform.AngleProperty, da)
    End Sub
    Private Sub Segundos()
        Dim da As New Animation.DoubleAnimation()
        da.From = 0
        da.[To] = 360
        da.Duration = New Duration(TimeSpan.FromSeconds(60))
        da.RepeatBehavior = Animation.RepeatBehavior.Forever
        Dim rt As New RotateTransform()
        Rectangle2.RenderTransform = rt
        rt.BeginAnimation(RotateTransform.AngleProperty, da)
    End Sub
    Private Sub Horas()
        Dim da As New Animation.DoubleAnimation()
        da.From = 0
        da.[To] = 360
        da.Duration = New Duration(TimeSpan.FromSeconds(3600 * 60))
        da.RepeatBehavior = Animation.RepeatBehavior.Forever
        Dim rt As New RotateTransform()
        Rectangle3.RenderTransform = rt
        rt.BeginAnimation(RotateTransform.AngleProperty, da)
    End Sub

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        Segundos()
        Minit()
        Horas()
    End Sub
End Class

Agora basta executar o projeto para ver o relógio:

Pegue o projeto completo aqui: RelogioAnimado.zip

Aguarde mais artigos sobre o assunto animação com WPF.

Rom 8:26 Do mesmo modo também o Espírito nos ajuda na fraqueza; porque não sabemos o que havemos de pedir como convém, mas o Espírito mesmo intercede por nós com gemidos inexprimíveis.
Rom 8:27
E aquele que esquadrinha os corações sabe qual é a intenção do Espírito: que ele, segundo a vontade de Deus, intercede pelos santos.

Rom 8:28
E sabemos que todas as coisas concorrem para o bem daqueles que amam a Deus, daqueles que são chamados segundo o seu propósito.

Referências:


José Carlos Macoratti