17 março, 2009

Animando Inteiros em Silverlight

O Silverlight 2 tem suporte nativo para animar apenas alguns tipos de dados (Double, Point, Color e em casos especiais, Object) utilizando Storyboards. Normalmente isso não é uma limitação, já que na maioria dos casos os tipos de dados suportados são suficientes, mas há um caso específico de uma propriedade que é bastante usada e não pode ser animada pois seu tipo não é suportado pelas animações padrão. Essa propriedade é Canvas.ZIndex, que é o tipo Integer.

Quando é necessário animar o Canvas.ZIndex de um controle em Silverlight, normalmente somos obrigados a programar a animação em um loop ou utilizando um timer, mudando bastante o modelo de programação que normalmente utilizariamos.

Eu queria ser capaz de fazer algo assim:

   1: IntegerAnimation animacao = new IntegerAnimation(); 
   2: Image img = new Image(); 
   3: Storyboard.SetTarget(animacao, img); 
   4: animacao.SetValue(Storyboard.TargetPropertyProperty, 
   5:                   new PropertyPath("(Canvas.ZIndex)"));

Mas depois de muito pesquisar, eu descobri que não é possível criar sua própria animação customizada de Integer (ou qualquer outro tipo) herdando de Timeline (que é a classe pai das animações citadas acima). Isso ocorre pois a implementação dessas animações não foi feita no CLR mas sim no plug-in.


No final das contas eu acabei conseguindo fazer de uma forma um pouco menos elegante do que eu queria, mas, melhor do que as alternativas (loop ou timer). Segue abaixo o código fonte de uma classe que faz a ponte entre uma animação de double e uma propriedade integer que se deseje animar.




   1: public class IntegerAnimationBridge : DependencyObject { 
   2:  
   3:     public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", 
   4:         typeof(double), 
   5:         typeof(IntegerAnimationBridge), 
   6:         new PropertyMetadata(new PropertyChangedCallback(IntegerAnimationBridge.OnValuePropertyChanged))); 
   7:  

   8:     public double Value { 
   9:         get { 
  10:             return (double)base.GetValue(ValueProperty); 
  11:         } 
  12:         set { 
  13:             base.SetValue(ValueProperty, value); 
  14:         } 
  15:     } 
  16:  
  17:     private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
  18:         IntegerAnimationBridge o = d as IntegerAnimationBridge; 
  19:         if (o != null && o.Target != null && o.TargetProperty != null) { 
  20:             o.Target.SetValue(o.TargetProperty, (int)Math.Floor(o.Value)); 
  21:         } 
  22:     } 
  23:  
  24:     private void TargetInfoChanged() { 
  25:         if (this.target != null && this.targetProperty != null) { 
  26:             int a; 
  27:             double b; 
  28:             a = (int)this.Target.GetValue(this.TargetProperty); 
  29:             b = a; 
  30:             this.Value = b; 
  31:         } 
  32:     } 
  33:  
  34:     private DependencyProperty targetProperty = null; 
  35:     public DependencyProperty TargetProperty { 
  36:         get { return targetProperty; } 
  37:         set { 
  38:             this.targetProperty = value; 
  39:             TargetInfoChanged(); 
  40:         } 
  41:     } 
  42:  
  43:     private DependencyObject target = null; 
  44:     public DependencyObject Target { 
  45:         get { return target; } 
  46:         set { 
  47:             this.target = value; 
  48:             TargetInfoChanged(); 
  49:         } 
  50:     } 
  51:  
  52: }

Utilizando essa classe, a animação acima fica assim:




   1: DoubleAnimation animacao = new DoubleAnimation(); 
   2: IntegerAnimationBridge ponte = new IntegerAnimationBridge(); 
   3: ponte.Target = new Image(); 
   4: ponte.TargetProperty = Canvas.ZIndexProperty; 
   5: Storyboard.SetTarget(animacao, ponte); 
   6: animacao.SetValue(Storyboard.TargetPropertyProperty, 
   7:                   new PropertyPath("(ZIndexAnimationBridge.Value)")); 

O que acontece agora é que a animação de Double afeta a propriedade Value do objeto ponte, que por sua vez atualiza a propriedade Canvas.ZIndex do objeto do tipo Image que está em seu target.


Chega de fazer loop para animar propriedades Integer!

Nenhum comentário: