[WPF/xaml]Storyboardでアニメーションをつくる2(TargetPropertyの階層的な指定)

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

**Storyboard関連**
- [[WPF/xaml] xaml＋C#で当番決めのためのルーレットを作る](https://qiita.com/tera1707/items/2a94c262c93b93854688#_reference-8730f3e1be562848669f)
- [[WPF/xaml]Storyboardでアニメーションをつくる](https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b)
- [[WPF/xaml]Storyboardでアニメーションをつくる2(TargetPropertyの階層的な指定)](https://qiita.com/tera1707/items/ee6d72bb68a8e2131082)

# やりたいこと
[以前、](https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b)Storyboardでアニメーションをつくる方法を勉強したが、その中の「DoubleAnimationUsingKeyFrames」でTargetPropertyを指定している部分の、

```a.xaml
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
```
の「(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)」の部分が、どういうことを書いているのか実はよくわかってなかった。
この際はっきりさせたい。

以前のコードはこちら参照
https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b

# 調べた時に見たサイト
こちらに、知りたいことはすべて書いてあった。

アニメーション（WPF）（++C++）
https://ufcpp.net/study/dotnet/wpf_xamlani.html
＞「TergetProperty を階層的に指定」
＞「TergetProperty で配列的にアクセス」

書いてあったが、見るだけでは勉強にならないので、自分なりの理解を書いてみようと思う。

# 自分なりの理解

### 別の書き方
①`Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"`
と
②`Storyboard.TargetProperty="RenderTransform.Children[0].X"`
は、同じことを表している。

②を省略せずに正しく書くと、①の書き方になる。

### ②の意味合い
②は、日本語でいうと、下記のようなことを表している。

- 対象(ここでは`Rectangle(名前はslider)`)に含まれている、
- `RenderTransform`にセットされているもの(`Children`の0番目)の、
- `X`をターゲットプロパティにする。

### ②で省略していること
②の書き方では。いろいろと省略されていることがある。

- `RenderTransform`は正しく言うと、`UIElement`クラスで定義されているメンバ`RenderTransform`である。
- `Children`は正しく言うと、`TransformGroup`クラスで定義されているメンバ`Children`である。
- `X`は正しく言うと、`TranslateTransform`クラスで定義されているメンバ`X`である。

### ①の意味合い

で、書き方としては、省略しているものを省略無しで正しく書こうとするとカッコ()を付けるようなので、

- `RenderTransform`　→　`(UIElement.RenderTransform)`
- `Children`　　　　　→　`(TransformGroup.Children)`
- `X`　　　　　　　　　→　`(TranslateTransform.X)`

となり、それぞれをつなげると、つまり①の内容になる。

**21/05/30追記**
★カッコを付けるのは、下記のためかも。

> 添付プロパティにバインドするには、添付プロパティをかっこで囲みます。 たとえば、添付プロパティ DockPanel.Dock にバインドする構文は Path=(DockPanel.Dock) です。

https://docs.microsoft.com/ja-jp/dotnet/desktop/wpf/data/binding-declarations-overview?view=netdesktop-5.0#binding-path-syntax



# 前提

①または②のように書くと、Storyboardは動いてくれるのだが、前提として対象のRectangle(名前はslider)の`RenderTransform`に、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/150107bb-a665-049e-979a-a464bff45535.png)
「TranslateTransform」をセットした「TransformGroup」がセットされてないと、例外で動かなかった。これは、`"RenderTransform.Children[0].X"`の中の右半分、「.Children[0].X」が存在しなかったからと思われる。

自分で意図してセットしないと、RectangleのRenderTransformにはなにも入っていないので、Storyboardでターゲットプロパティとして指定するためには、**「Storyboard動作時に」**TranslateTransformが入ったTransformGroupがセットされていることが前提となる。
(セットしてなくても、アプリ起動時には落ちない。Storyboard動作時に落ちる。)

# (UIElement.RenderTransform)の「UIElement」はどこから来た？
今回のstoryboardのターゲットはRectangleで、そのRectangleは「UIElement」を継承してできている。

Rectangle Class
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.shapes.rectangle?view=netframework-4.8
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/c8847830-2023-3734-faa4-e7b35a19933c.png)

UIElementクラスで「RenderTransform」は定義されいているので、「(UIElement.RenderTransform)」と書く。

※ためしに、`(UIElement.RenderTransform)`のところを`(FrameworkElement.RenderTransform)`とか`(Shape.RenderTransform)`とか書いても動くのは動く。

# 自分なりの結論

```a.xaml
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)">
```

上記のStoryboard.TargetPropertyの書き方は、省略をせずに、階層的にプロパティを書いたものだった。

# 別の例
別の例として、背景の色を時間を追って変化させたいときに、下記のようにColorAnimationUsingKeyFrames  を書いたとする。

```a.xaml
<ColorAnimationUsingKeyFrames  BeginTime="00:00:00"  Storyboard.TargetName="BackgroundBorder" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)">
```

この中のStoryboard.TargetPropertyは、

- 対象に含まれている、
- `Background`にセットされているもの(`SolidColorBrush`)の、
- `Color`をターゲットプロパティにする。

ということ。省略して書くと、

```a.xaml
<ColorAnimationUsingKeyFrames  BeginTime="00:00:00"  Storyboard.TargetName="BackgroundBorder" Storyboard.TargetProperty="Background.Color">
```
となる。またこの場合も、対象(ここではBackgroundBorderという名前のBorder)のBackgroundがあらかじめ設定されていないと例外で落ちる。

```この書き方をAttributeSyntaxというらしい.xaml
<!-- ここで、Background="Gray"がないとStoryboard実行時に落ちる -->
<Border Name="BackgroundBorder" CornerRadius="2" Height="28" Width="60" BorderBrush="White" BorderThickness="2" Background="Gray"/>
```

`Background="Gray"`は、Border のBackgroundに、グレー色の`SolidColorBrush`がセットされている、ということを示す。
(xamlが勝手にそういうことをしてくれている)

それがないと落ちてしまうのは、Storyboardが変化させようとする`Color`を持っている`SolidColorBrush`が存在しないからだと思われる。

※**「`Background="Gray"`は、Border のBackgroundに、グレー色の`SolidColorBrush`がセットされている」**というのは、個人的には下記のように書くとイメージが湧きやすかった。

```この書き方を「PropertyElementSyntax」というらしい.xaml
<Border Name="BackgroundBorder" CornerRadius="2" Height="28" Width="60" BorderBrush="White" BorderThickness="2">
    <Border.Background>
        <SolidColorBrush Color="Gray"/>   <!-- ←これがないと、storyboardが設定するものがなくて落ちてしまう。 -->
    </Border.Background>
</Border>
```

# 参照

アニメーション（WPF）（++C++）
https://ufcpp.net/study/dotnet/wpf_xamlani.html
＞「TergetProperty を階層的に指定」
＞「TergetProperty で配列的にアクセス」

XAML の基本構造（WPF）
Attribute SyntaxとかProperty Element Syntaxとか
https://ufcpp.net/study/dotnet/wpf_xamlbasic.html?key=attribute#attribute
＞「プロパティの設定」