目次
- WPFのCustomControlでアニメーション-概要
- LineにLinearGradientBrushを適用したものをCustomControlとして作る
- Storyboardを作成する(XAML)
- Storyboardを切り替える(XAML)
- Storyboardを切り替える(Code Behind)
- Storyboardを作成する(Code Behind)←ここ
目的
Code BehindでStoryboardを作成する
作成
.cs
- Storyboardを作成する
private void UpdateStoryboard() { if (!(GetLineObject() is Line line)) return; gradientAnimationTrue?.Remove(line); gradientAnimationFalse?.Remove(line); if (AnimationType) gradientAnimationTrue?.Begin(line, true); else gradientAnimationFalse?.Begin(line, true); } private Storyboard gradientAnimationTrue = default; private Storyboard gradientAnimationFalse = default; private void CreateStoryboard() { var line = GetLineObject(); var targetOffset = "(Line.Stroke).(LinearGradientBrush.GradientStops)[{0}].(GradientStop.Offset)"; var duration = new Duration(TimeSpan.FromSeconds(1)); var dat = new DoubleAnimation(); dat.Duration = duration; dat.From = 0; dat.By = 1; Storyboard.SetTarget(line, dat); Storyboard.SetTargetProperty(dat, new PropertyPath(string.Format(targetOffset, 1))); gradientAnimationTrue = new Storyboard() { AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever }; gradientAnimationTrue.Children.Add(dat); var daf0 = new DoubleAnimation(0,1,duration); Storyboard.SetTarget(line, daf0); Storyboard.SetTargetProperty(daf0, new PropertyPath(string.Format(targetOffset, 0))); var daf1 = new DoubleAnimation(0, 1, duration); Storyboard.SetTarget(line, daf1); Storyboard.SetTargetProperty(daf1, new PropertyPath(string.Format(targetOffset, 1))); gradientAnimationFalse = new Storyboard() { AutoReverse = true, RepeatBehavior = RepeatBehavior.Forever }; gradientAnimationFalse.Children.Add(daf0); gradientAnimationFalse.Children.Add(daf1); } private Line GetLineObject() => this.GetTemplateChild("gradientLine") as Line; public override void OnApplyTemplate() { base.OnApplyTemplate(); CreateStoryboard(); UpdateStoryboard(); }
Generic.xaml
- Storyboardを削除する
<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}"> <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>
注意点
SetTargetで指定できるのはFrameworkElement/FrameworkContentElementっぽい?(line.strokeとかを指定してもエラーは出ないけどアニメーションが動作しなかった)
SetTargetNameではFrameworkElement, FrameworkContentElement, Freezableを指定できる。
その他
targetpropertyについて
省略する場合
var targetOffset = "Stroke.GradientStops[{0}].Offset";
PropertyPath(String, Object[])
使う場合
var targetPatht = "(0).(1)[{0}].(2)"; var dps = new DependencyProperty[]{ Line.StrokeProperty, LinearGradientBrush.GradientStopsProperty, GradientStop.OffsetProperty }; Storyboard.SetTargetProperty(dat, new PropertyPath(string.Format(targetPatht, 1), dps));
targetについて
Storyboard.SetTargetName(dat, "gradientLine");
でもよい。
他適当に名前を付けてれば適当に使える。
<GradientStop x:Name="gs1" Color="{Binding RightColor,RelativeSource={RelativeSource TemplatedParent}}" Offset="1"/>
とすれば
Storyboard.SetTargetName(dat, "gs1"); Storyboard.SetTargetProperty(dat, new PropertyPath(GradientStop.OffsetProperty));
とできる。
参考
The object must be a FrameworkElement, FrameworkContentElement, or Freezable.
おまけ
LinearGradientBrush
もCode Behindで作れば柔軟な操作ができる。線長に合わせてGradientStop
を増やすとか。
private void CreateLinearGradientBrush() { var gradientStops = new GradientStopCollection() { new GradientStop(LeftColor,0), new GradientStop(RightColor,1), }; GetLineObject().Stroke = new LinearGradientBrush(gradientStops); } public override void OnApplyTemplate() { base.OnApplyTemplate(); CreateLinearGradientBrush(); CreateStoryboard(); UpdateStoryboard(); }
作成したオブジェクトはthis.RegisterName
で登録しておくと便利。
ただしNameScope.SetNameScope(this,new NameScope());
で独自のNameScope
を作成しておくこと。
作成しておかないとCustomControlを複数配置した場合、名前が被ってエラーになる。これはrootのNamescopeに登録されるため。たぶん。