ViewModelのプロパティ変更をトリガーにアニメーションする方法
WPF でViewModel のプロパティが変更されたときにStoryboard を開始する方法で少し躓いたのでメモ。
開発環境は以下の通り。
- OS : Windows 10 64bit
- .NET Framework : 4.6
- Visual Studio : 2017
- GUIアーキテクチャ : WPF
ViewModel のプロパティの変更をトリガーにアニメーションするのが目的なので、まず思い浮かんだのがこれ↓
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:BindingSoureChangedAnimation" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="BindingSoureChangedAnimation.MainWindow" Title="MainWindow" Width="525" Height="350" d:DataContext="{d:DesignInstance {x:Type local:MainWindowViewModel}, IsDesignTimeCreatable=True}" mc:Ignorable="d"> <Window.Style> <!-- Style のDataTrigger でViewModel のプロパティの値によってストーリーボードを開始する。 --> <Style TargetType="{x:Type Window}"> <Style.Triggers> <DataTrigger Binding="{Binding IsAnimation}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard Storyboard="{DynamicResource ColorChangeStoryboard}" x:Name="Begin_ColorChangeStoryboard" /> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <StopStoryboard BeginStoryboardName="Begin_ColorChangeStoryboard" /> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers> </Style> </Window.Style> <Window.Resources> <!-- 実行したいStoryboard --> <Storyboard x:Key="ColorChangeStoryboard"> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle"> <EasingColorKeyFrame KeyTime="0" Value="#FF7A7AFF" /> <EasingColorKeyFrame KeyTime="0:0:1" Value="#FFFFFFA9" /> </ColorAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <CheckBox Content="アニメーション" IsChecked="{Binding IsAnimation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> </CheckBox> <Border Grid.Row="1"> <Rectangle x:Name="rectangle" Fill="#FF7A7AFF" Stroke="Black" Margin="32" /> </Border> </Grid> </Window>
Window のStyle.Trigger のDataTrigger を使用するパターン。
これを実行するとこうなります。
Window のStyle からでは、Window.Resources 内のリソースにはアクセスできないとのこと…。
同様にWindow.Content のGrid のStyle でも実現はできません。
で、次の案。
Behavior のControlStoryboardAction を使用する。
ただ、このBehavior はEventTrigger になっているのでイベントの着火をトリガーとするため今回の目的には合致しません。(※)
いろいろ調べた結果、以下の方法で実現することができました。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:BindingSoureChangedAnimation" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="BindingSoureChangedAnimation.MainWindow" Title="MainWindow" Width="525" Height="350" d:DataContext="{d:DesignInstance {x:Type local:MainWindowViewModel}, IsDesignTimeCreatable=True}" mc:Ignorable="d"> <Window.Resources> <!-- 実行したいStoryboard --> <Storyboard x:Key="ColorChangeStoryboard" RepeatBehavior="Forever"> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle"> <EasingColorKeyFrame KeyTime="0" Value="#FF7A7AFF" /> <EasingColorKeyFrame KeyTime="0:0:1" Value="#FFFFFFA9" /> </ColorAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <i:Interaction.Triggers> <!-- IsAnimation プロパティの値が"True" ならStoryboard を開始する。 "True" 以外の値ならStoryboard を停止する。 --> <ei:DataTrigger Binding="{Binding IsAnimation, Mode=OneWay}" Value="True"> <ei:ControlStoryboardAction Storyboard="{StaticResource ColorChangeStoryboard}" /> </ei:DataTrigger> <ei:DataTrigger Binding="{Binding IsAnimation, Mode=OneWay}" Value="True" Comparison="NotEqual"> <ei:ControlStoryboardAction Storyboard="{StaticResource ColorChangeStoryboard}" ControlStoryboardOption="Stop" /> </ei:DataTrigger> </i:Interaction.Triggers> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <CheckBox Content="アニメーション" IsChecked="{Binding IsAnimation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <Border Grid.Row="1"> <Rectangle x:Name="rectangle" Fill="#FF7A7AFF" Stroke="Black" Margin="32" /> </Border> </Grid> </Window>
変更点はここ
<i:Interaction.Triggers> <!-- IsAnimation プロパティの値が"True" ならStoryboard を開始する。 "True" 以外の値ならStoryboard を停止する。 --> <ei:DataTrigger Binding="{Binding IsAnimation, Mode=OneWay}" Value="True"> <ei:ControlStoryboardAction Storyboard="{StaticResource ColorChangeStoryboard}" /> </ei:DataTrigger> <ei:DataTrigger Binding="{Binding IsAnimation, Mode=OneWay}" Value="True" Comparison="NotEqual"> <ei:ControlStoryboardAction Storyboard="{StaticResource ColorChangeStoryboard}" ControlStoryboardOption="Stop" /> </ei:DataTrigger> </i:Interaction.Triggers>
ControlStoryboardAction 使ってるんですが、そのトリガーに"ei:DataTrigger" を使用しています。
通常の"DataTrigger" とは違って"Microsoft.Expression.Interactivity.Core" 名前空間にいるやつです。
こいつを探し出すのが苦労したorz
通常、Blend からBehavior をドラッグ・アンド・ドロップで配置するとトリガーは、"EventTrigger" になります。
これを"ei:DataTrigger" に変更するには以下の手順を実行します。
1.Behavior を配置する。
2.EventTrigger をDataTrigger に変更する。
プロパティ ウィンドウの[トリガー] - [新規] をクリックします。
一覧から"DataTrigger" を選択して[OK] をクリックします。
3.トリガーの値を設定する。
プロパティ ウィンドウの[トリガー] "TriggerType" が"DataTrigger" に変わっていることを確認してViewModel のプロパティの値をバインドする。
これを探し出すのに一番時間がかかりました。
ちなみにControlStoryboardAction は、Play とStop それぞれのアクションを設定しないとStoryboard を開始、終了することができません。
また、値の比較種別として"Equal", "NotEqual" 以外にも"LessThan (未満)", "GreaterThan (~より大きい)" 等があるので、ViewModel のプロパティがある数値以上の場合にアニメーションを制御する といったこともできそうです。(未確認)
今回使用したソースはこちら↓↓↓
github.com