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!