目次
- WPFのCustomControlでアニメーション-概要
- LineにLinearGradientBrushを適用したものをCustomControlとして作る
- Storyboardを作成する(XAML)
- Storyboardを切り替える(XAML)
- Storyboardを切り替える(Code Behind)←ここ
- Storyboardを作成する(Code Behind)
目的
Code Behindでプロパティによって動作するStoryboardを切り替える
作成
.cs
- 依存関係プロパティ
AnimationType
が変更されたときに使用するStoryboardを切り替える。
public bool AnimationType { get { return (bool)GetValue(AnimationTypeProperty); } set { SetValue(AnimationTypeProperty, value); } } public static readonly DependencyProperty AnimationTypeProperty = DependencyProperty.Register(nameof(AnimationType), typeof(bool), typeof(This), new PropertyMetadata(default(bool), AnimationTypeChanged)); public static void AnimationTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((This)d).UpdateStoryboard(); } private Storyboard storyBoard = default; private void UpdateStoryboard() { if (!(this.GetTemplateChild("gradientLine") is Line line)) return; storyBoard?.Remove(line); if (AnimationType) storyBoard = this.Template?.Resources["gradientAnimationTrue"] as Storyboard; else storyBoard = this.Template?.Resources["gradientAnimationFalse"] as Storyboard; storyBoard?.Begin(line, true); } public override void OnApplyTemplate() { base.OnApplyTemplate(); UpdateStoryboard(); }
Generic.xaml
<ControleTemplate.Triggers>
を削除
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GradientLineTest.Controls" > <Style TargetType="{x:Type local:GradientLineControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:GradientLineControl}"> <ControlTemplate.Resources> <Storyboard x:Key="gradientAnimationTrue" RepeatBehavior="Forever" AutoReverse="True"> <DoubleAnimation Storyboard.TargetName="gradientLine" Storyboard.TargetProperty="(Line.Stroke).(LinearGradientBrush.GradientStops)[1].(GradientStop.Offset)" From="0" To="1"/> </Storyboard> <Storyboard x:Key="gradientAnimationFalse" RepeatBehavior="Forever" AutoReverse="True"> <DoubleAnimation Storyboard.TargetName="gradientLine" Storyboard.TargetProperty="(Line.Stroke).(LinearGradientBrush.GradientStops)[0].(GradientStop.Offset)" From="0" To="1"/> <DoubleAnimation Storyboard.TargetName="gradientLine" Storyboard.TargetProperty="(Line.Stroke).(LinearGradientBrush.GradientStops)[1].(GradientStop.Offset)" From="0" To="1"/> </Storyboard> </ControlTemplate.Resources> <Line x:Name="gradientLine" X1="{TemplateBinding X1}" Y1="{TemplateBinding Y1}" X2="{TemplateBinding X2}" Y2="{TemplateBinding Y2}" StrokeThickness="10" > <Line.Stroke> <LinearGradientBrush StartPoint="0.0,0.5" EndPoint="1.0,0.5"> <GradientStop Color="{Binding LeftColor,RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0"/> <GradientStop Color="{Binding RightColor,RelativeSource={RelativeSource TemplatedParent}}" Offset="1"/> </LinearGradientBrush> </Line.Stroke> </Line> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
注意点
namescopeについて
ControlTemplate中のリソースなどの取得は通常とは異なる。これはControlTemplateは独自のnamescopeを持つため。
今回はthis.GetTemplateChild("gradientLine")
を使ってるけどthis.Template.FindName("gradientLine", this)
でもよい。
Storyboardの取得
storyboardを<Line.Resources>
に書いた場合は
line.Resources["gradientAnimationTrue"]
で取得できる。
storyboardを<Style.Resources>
に書いた場合
this.FindResource("gradientAnimationFalse")
で取得できる。
Storyboardを操作するタイミングについて
テンプレート読み込みが完了した後に行う。OnApplyTemplate
を利用すると都合が良い。
参考
Templates in WPF have a self-contained namescope. This is because templates are re-used, and any name defined in a template cannot remain unique when multiple instances of a control each instantiate its template.
OnApplyTemplate is called after the template is completely generated and attached to the logical tree.
その他
Code BehindでStoryboardを操作できるようにしておくとSetSpeedRatio
でアニメーションの動作速度を柔軟に設定できる。XAMLからでも<SetStoryboardSpeedRatio>
で設定できるけどBindingしようとすると例外'SetStoryboardSpeedRatio' の 'SpeedRatio' プロパティで 'Binding' を設定することはできません。'Binding' は、DependencyObject の DependencyProperty でのみ設定できます。'
が出る。