trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

Storyboardを作成する(XAML)

目次

  1. WPFのCustomControlでアニメーション-概要
  2. LineにLinearGradientBrushを適用したものをCustomControlとして作る
  3. Storyboardを作成する(XAML)←ここ
  4. Storyboardを切り替える(XAML)
  5. Storyboardを切り替える(Code Behind)
  6. Storyboardを作成する(Code Behind)

ここでやること

XAML側でStoryboardを作成してGradientのoffsetを動かす

f:id:trsing:20210228223254g:plain

作成

.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>