目次
- WPFのCustomControlでアニメーション-概要
- LineにLinearGradientBrushを適用したものをCustomControlとして作る
- Storyboardを作成する(XAML)←ここ
- Storyboardを切り替える(XAML)
- Storyboardを切り替える(Code Behind)
- Storyboardを作成する(Code Behind)
ここでやること
XAML側でStoryboardを作成してGradientのoffsetを動かす
作成
.cs
何もしない
Generic.xaml
- GradientStopのoffsetを対象としたStoryboardを作る
- ロードイベントで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.Resources> <Storyboard x:Key="gradientAnimation" RepeatBehavior="Forever" AutoReverse="True"> <DoubleAnimation Storyboard.TargetName="gradientLine" Storyboard.TargetProperty="(Line.Stroke).(LinearGradientBrush.GradientStops)[1].(GradientStop.Offset)" From="0" To="1"/> </Storyboard> </Line.Resources> <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.Triggers> <EventTrigger RoutedEvent="Line.Loaded"> <BeginStoryboard Storyboard="{StaticResource gradientAnimation}"/> </EventTrigger> </Line.Triggers> </Line> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
気になった点
- なんで
Storyboard.TargetProperty="(Line.Stroke).(LinearGradientBrush.GradientStops)[1].(GradientStop.Offset)"
みたいな書き方なの?
調べた結果
TargetProperty
にはTargetName
で指定したオブジェクトから操作目標へのパスを書く。
今回の場合、targetのlineから操作目標offsetへのパスはStroke.GradientStops[1].Offset
。これは省略形で正式に書くと(Line.Stroke).(LinearGradientBrush.GradientStops)[1].(GradientStop.Offset)
。
パフォーマンスにも影響があるらしい。
※Storyboard.TargetProperty="Stroke.GradientStops[1].Offset"
でも動く
参考
You can animate a property that is a sub-property of the target object. In other words, if there's a property of the target object that's an object itself, and that object has properties, you must define a property path that explains how to step through that object-property relationship. Whenever you are specifying an object where you want to animate a sub-property, you enclose the property name in parentheses, and you specify the property in typename.propertyname format.
https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/property-path-syntax
この例では、 ボタンクリック後の Rectangle の色を、 Storyboard.TargetProperty="Fill.Color" で指定しています。 これは、省略せずにきちんと書くなら、 Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" となります。 このように、TargetProperty には、階層的なプロパティの指定の仕方ができます。
https://ufcpp.net/study/dotnet/wpf_xamlani.html
リフレクションを使用することによるパフォーマンスへの影響を回避できるということです。
https://www.amazon.co.jp/dp/4798114200
その他
Storyboardを書く場所
Storyboardは<Style.Resources>
や<ControlTemplate.Resources>
に書いても動く。どこに書くのが良いのかはよくわからない。ただ上から順次解釈される(とどっかに書いてあった気がする)ため使用する場所(<Line.Triggers>
)より上の行で定義してないとエラーになる。
pathについて
Storyboard.TargetProperty
はtargetからのパスなので、例えば操作するGradientStopに名称(gs1)をつけてtargetにすればかなり短くできる。
<Line.Resources> <Storyboard x:Key="gradientAnimation" RepeatBehavior="Forever" AutoReverse="True"> <DoubleAnimation Storyboard.TargetName="gs1" Storyboard.TargetProperty="Offset" From="0" To="1"/> </Storyboard>