2009-10-30 22 views
8

動的な値を持つアニメーションを開始するためのきれいな方法を探しています。基本的には、ある要素が別の要素のデータに基づいて幅を変更するアニメーションを行いたいとします。テキストプロパティがバインドされているTextBlockがあるとします。このプロパティが変更されたら、DoubleAnimationを使用して幅を以前の値から新しい値に変更するためのRectangleのビジュアルエレメントが必要になります。WPF MVVMプロパティの変更アニメーション

可能であれば、コードを私の視点に入れないようにしています。私はDataTriggersを調べましたが、Enumのような値が何であるかを知る必要があるようです。私の場合、ストーリーボードを起動する必要があるのは値の変更だけであり、アニメーションは現在の(以前の)値から開始し、新しい値にうまく移行する必要があります。

アイデアたぶん私は不動産を見逃してしまったかもしれない。

答えて

13

ここで私が解決した解決策があります。私のViewModelのデータに基づいてアニメーションを行うには、DataTriggerを使用しました。以下はコントロールのための私のスタイルです。アニメーションによって変更されたプロパティは、アニメーション「文脈」外に設定することができないため

<Style TargetType="Grid" x:Key="DetailRotation" > 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding Path=AnimationState}" Value="New"> 
      <DataTrigger.EnterActions> 
       <StopStoryboard BeginStoryboardName="EndAnimation" /> 
       <BeginStoryboard Name="NewAnimation"> 
        <Storyboard> 
         <ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,30,0,0" To="0,0,0,0" Duration="0:0:1" /> 
         <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1" /> 
        </Storyboard> 
       </BeginStoryboard> 
      </DataTrigger.EnterActions> 
      <DataTrigger.ExitActions> 

      </DataTrigger.ExitActions> 

     </DataTrigger> 
     <DataTrigger Binding="{Binding Path=AnimationState}" Value="End"> 
      <DataTrigger.EnterActions> 
       <StopStoryboard BeginStoryboardName="NewAnimation" /> 
       <BeginStoryboard Name="EndAnimation"> 
        <Storyboard> 
         <ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,0,0" To="0,-20,0,0" Duration="0:0:1"/> 
         <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:1" /> 
        </Storyboard> 
       </BeginStoryboard> 
      </DataTrigger.EnterActions> 
     </DataTrigger> 

    </Style.Triggers> 
</Style> 
1

Attached Propertiesを使って探索して、必要なロジックをあなたが望むストーリーボード/アニメーションに接続することができます。

これは必ずしもコードを記述する必要はありませんが、ビューから切り離された状態に保たれ、複数のビューにまたがって再利用されます。

+0

私は本当にこれが私が必要とする方向であるかどうかはわかりません。ストーリーボードを実行してPreviousWidthとCurrentWidthをアニメーションのtoプロパティとfromプロパティにバインドするように命令するために、ルーテーションを使用する必要があります。これはこれを行う唯一の方法かもしれません。まだわからないDataTriggersは、動的な変更ではなく、状態スタイルの変更に対してのみ機能します。これは私がパターンから少し離れてしまう傾向があるところです。 – cjibo

0

Iを効果的XAMLで同じことを行うことができなかったので、私は、コード溶液を思い付きました。

private void UserControl_IsVisibleChanged(object sender, 
    DependencyPropertyChangedEventArgs e) 
{ 
    if (this.Visibility == Visibility.Visible) 
    { 
     DoubleAnimation fadeIn = new DoubleAnimation(); 
     fadeIn.From = 1d; 
     fadeIn.To = 1d; 
     fadeIn.Duration = new Duration(new TimeSpan(0, 0, 0)); 

     DoubleAnimation fade = new DoubleAnimation(); 
     fade.From = 1d; 
     fade.To = 0d; 
     fade.BeginTime = TimeSpan.FromSeconds(5); 
     fade.Duration = new Duration(new TimeSpan(0, 0, 1)); 

     NameScope.SetNameScope(this, new NameScope()); 
     this.RegisterName(this.Name, this); 

     Storyboard.SetTargetName(fadeIn, this.Name); 
     Storyboard.SetTargetProperty(fadeIn, new PropertyPath 
      (UIElement.OpacityProperty)); 

     Storyboard.SetTargetName(fade, this.Name); 
     Storyboard.SetTargetProperty(fade, new PropertyPath 
      (UIElement.OpacityProperty)); 

     Storyboard sb = new Storyboard(); 
     sb.Children.Add(fadeIn); 
     sb.Children.Add(fade); 

     sb.Completed += new EventHandler(sb_Completed); 
     sb.Begin(this); 
    } 
} 

void sb_Completed(object sender, EventArgs e) 
{ 
    this.Visibility = Visibility.Hidden; 
} 
0

実はあなたはViewModelプロパティにDoubleAnimation.ToPropertyを結合して、実際のコントロールをアニメーション化します。問題は、ToPropertyが変更されたときにアニメーションを続けることです。私の解決策は、を包むMarkupExtenstionにこのすべてのロジックをカプセル化します。

public class AnimateBindingExtension : MarkupExtension { 
    static DependencyPropertyDescriptor dpd = 
     DependencyPropertyDescriptor.FromProperty(DoubleAnimation.ToProperty, 
      typeof(DoubleAnimation)); 

    public AnimateBindingExtension(PropertyPath path) { 
     Path = path; 
    } 

    public bool ValidatesOnExceptions { get; set; } 
    public IValueConverter Converter { get; set; } 
    public object ConverterParamter { get; set; } 
    public string ElementName { get; set; } 
    public RelativeSource RelativeSource { get; set; } 
    public object Source { get; set; } 
    public bool ValidatesOnDataErrors { get; set; } 
    [ConstructorArgument("path")] 
    public PropertyPath Path { get; set; } 
    public object TargetNullValue { get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) { 
     var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 

     if (valueProvider == null) { 
      throw new Exception("could not get IProviderValueTarget service."); 
     } 

     var bindingTarget = valueProvider.TargetObject as FrameworkElement; 
     var bindingProperty = valueProvider.TargetProperty as DependencyProperty; 

     if (bindingProperty == null || bindingTarget == null) { 
      throw new Exception(); 
     } 

     var binding = new Binding { 
      Path = Path, 
      Converter = Converter, 
      ConverterParameter = ConverterParamter, 
      ValidatesOnDataErrors = ValidatesOnDataErrors, 
      ValidatesOnExceptions = ValidatesOnExceptions, 
      TargetNullValue = TargetNullValue 
     }; 

     if (ElementName != null) binding.ElementName = ElementName; 
     else if (RelativeSource != null) binding.RelativeSource = RelativeSource; 
     else if (Source != null) binding.Source = Source; 

     // you can add a Duration property to this class and use it here 
     var anim = new DoubleAnimation { 
      Duration = new Duration(TimeSpan.FromSeconds(0.1)), 
      AccelerationRatio = 0.2, 
      DecelerationRatio = 0.8 
     }; 
     // this can be a new subclass of DoubleAnimation that 
     // overrides ToProperty metadata and add a property 
     // change callback 
     dpd.AddValueChanged(anim, (s, e) => bindingTarget.BeginAnimation(bindingProperty, anim)); 

     BindingOperations.SetBinding(anim, DoubleAnimation.ToProperty, binding); 
     // this is because we need to catch the DataContext so add animation object 
     // to the visual tree by adding it to target object's resources. 
     bindingTarget.Resources[bindingProperty.Name] = anim; 
     // animation will set the value 
     return DependencyProperty.UnsetValue; 
    } 
} 

他のアニメーションクラスと同じことを行い、他のタイプをアニメーションできます。

+0

これは、スタイル/テンプレートに配置したときにストーリーボードをフリーズさせるという一般的な問題につながりますか?非常に面白くない場合は... – tobriand

+0

Xamlでこのバインディング拡張を使用するための構文は何ですか? –

関連する問題