none
ControlTemplate.Triggers による Storyboard の実行について RRS feed

  • 質問

  • いつもお世話になっております。

    WPF の ControlTemplate.Triggers による Storyboard の実行について質問させていただきます。


    目的
    次の状態遷移をする際にアニメーションを実行する Button の ControlTemplate を作成すること。
    ・Normal -> IsPressed
    ・IsPressed -> Normal
    ・Normal -> IsDisabled
    ・IsDisabled -> Normal
    また、今回は ControlTemplate.Triggers の仕様を理解したいのでアニメーションの実行には ControlTemplate.Triggers のみを使用し、
    かつ、Xaml のみでどこまでできるのかを確かめるために C# のコードは記述しないこととしています。

    設定した Trigger
    (コメントになっている部分は実際に使用したコードでは Background の色が変化する Storyboard が指定されています。
    Xamlの全文は最後に記載しますのでよろしければそちらもご覧ください)

    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsEnabled" Value="True"/>
            <Condition Property="IsPressed" Value="True"/>
        </MultiTrigger.Conditions>
        <MultiTrigger.ExitActions>
            <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
        </MultiTrigger.ExitActions>
        <MultiTrigger.EnterActions>
            <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
        </MultiTrigger.EnterActions>
    </MultiTrigger>
    
    <Trigger Property="IsEnabled" Value="False">
        <Trigger.ExitActions>
            <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
        </Trigger.ExitActions>
        <Trigger.EnterActions>
            <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
        </Trigger.EnterActions>
    </Trigger>


    問題となった操作
    1. 定義した ControlTemplate を IsEnabled の初期値を True に設定した Button の Template に設定しコンパイル、実行をします。
    2. IsEnabled == True の Button 上でマウスの左ボタンを Down すると MultiTrigger.EnterActions に登録した Storyboard が実行されます。
    3. マウスの左ボタンを Up すると MultiTrigger.ExitActions に登録した Storyboard が実行されます。
    4. 2. と 3. はこの時点では何度実行しても正常に動作します。
    5. Button の IsEnabled を False に設定します。このとき Trigger.EnterActions に登録した Storyboard が実行されます。
    6. この段階で 2. を実行すると条件が満たされないため、Storyboard は実行されません。
    7. Button の IsEnabled を True に設定します。このとき Trigger.ExitActions に登録した Storyboard が実行されます。
    8. 2.をもう一度実行します。すると条件は満たされているはずなのに Storyboard が実行されませんでした。

    お教えいただきたいこと
    a. なぜ上の操作を行った場合、8.の操作で Storyboard が実行されないのか
    b. ControlTemplate.Triggers のみかつ Xaml のみという条件で今回の目的としているような Button の ControlTemplate を作成することは可能であるか

    すでに確認をしたこと
    IsEnabled が False -> True に変化した際に、何かしらの理由で IsPressed が True にならなくなってしまったのかと思い、
    Timer で一定時間ごとに Button の IsPressed プロパティの値を確認しましたが、正常に変化していました。

    環境
    OS : Windows 10
    開発環境 :
    Visual Studio Community 2015
    Microsoft Blend for Visula Studio Community 2015
    .Net Framework 4.5.2

    最後に Xaml 全文を記載します。

    <Window x:Class="WpfApplication4.MainWindow"
            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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication4"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="StoryboardNormal">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                            <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Black"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="StoryboardIsPressed">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                            <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF0017FF"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="StoryboardIsDisabled">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                            <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF8B8B8B"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </ControlTemplate.Resources>
                <Border x:Name="border" Background="Black"/>
                <ControlTemplate.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.ExitActions>
                            <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
                            <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
                        </MultiTrigger.ExitActions>
                        <MultiTrigger.EnterActions>
                            <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
                            <BeginStoryboard x:Name="StoryboardIsPressed_BeginStoryboard" Storyboard="{StaticResource StoryboardIsPressed}"/>
                        </MultiTrigger.EnterActions>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsEnabled" Value="True"/>
                            <Condition Property="IsPressed" Value="True"/>
                        </MultiTrigger.Conditions>
                    </MultiTrigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Trigger.ExitActions>
                            <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
                            <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
                        </Trigger.ExitActions>
                        <Trigger.EnterActions>
                            <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
                            <BeginStoryboard x:Name="StoryboardIsDisabled_BeginStoryboard" Storyboard="{StaticResource StoryboardIsDisabled}"/>
                        </Trigger.EnterActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Window.Resources>
        <Grid>
            <Button x:Name="button" Content="Button" Margin="140,131,151.667,98.667" Template="{DynamicResource ButtonControlTemplate1}" IsEnabled="{Binding IsChecked, ElementName=checkBox, Mode=OneWay}"/>
            <CheckBox x:Name="checkBox" Content="Button Is Enabled" Height="20" Margin="186.667,81,188.333,0" VerticalAlignment="Top" IsChecked="True"/>
    
        </Grid>
    </Window>
    




    以上、よろしくお願いいたします。

    2017年2月9日 12:23

回答

  • 複数のトリガーで同時に条件を満たした場合、後のAnimationで上書きされます。
    また、アニメーションはFillBehaviorによりアニメーションの最終状態を維持するか、停止するかが決まります。
    そのため、チェックボックスをUncheck->CheckするとIsEnabledのExitActionのアニメーションが実行されて、その影響が持続してしまいます。

    直すにはトリガーの順番を変えるか、FillBehavior=Stopに変えたトリガーを使います。

    <ControlTemplate.Triggers>
    
        <Trigger Property="IsEnabled" Value="False">
            <Trigger.ExitActions>
                <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
                <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
            </Trigger.ExitActions>
            <Trigger.EnterActions>
                <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
                <BeginStoryboard x:Name="StoryboardIsDisabled_BeginStoryboard" Storyboard="{StaticResource StoryboardIsDisabled}"/>
            </Trigger.EnterActions>
        </Trigger>
    
        <MultiTrigger>
            <MultiTrigger.ExitActions>
                <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
                <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
            </MultiTrigger.ExitActions>
            <MultiTrigger.EnterActions>
                <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
                <BeginStoryboard x:Name="StoryboardIsPressed_BeginStoryboard" Storyboard="{StaticResource StoryboardIsPressed}"/>
            </MultiTrigger.EnterActions>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True"/>
                <Condition Property="IsPressed" Value="True"/>
            </MultiTrigger.Conditions>
        </MultiTrigger>
    
    </ControlTemplate.Triggers>
    あるいは
    <ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
        <ControlTemplate.Resources>
            <Storyboard x:Key="StoryboardNormal">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Black"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
    
            <Storyboard x:Key="StoryboardNormal_FillBehavior_Stop">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border"
                                    FillBehavior="Stop"><!-- 最後まで行ったら終わりにする -->
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Black"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
    
            <Storyboard x:Key="StoryboardIsPressed">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF0017FF"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
            <Storyboard x:Key="StoryboardIsDisabled">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border" >
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF8B8B8B"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
        </ControlTemplate.Resources>
        <Border x:Name="border" Background="Black"/>
        <ControlTemplate.Triggers>
            <MultiTrigger>
                <MultiTrigger.ExitActions>
                    <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
                    <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
                </MultiTrigger.ExitActions>
                <MultiTrigger.EnterActions>
                    <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
                    <BeginStoryboard x:Name="StoryboardIsPressed_BeginStoryboard" Storyboard="{StaticResource StoryboardIsPressed}"/>
                </MultiTrigger.EnterActions>
                <MultiTrigger.Conditions>
                    <Condition Property="IsEnabled" Value="True"/>
                    <Condition Property="IsPressed" Value="True"/>
                </MultiTrigger.Conditions>
            </MultiTrigger>
    
            <Trigger Property="IsEnabled" Value="False">
                <Trigger.ExitActions>
                    <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
                    <!-- FillBehavior="Stop"のAnimationを適用する -->
                    <BeginStoryboard Storyboard="{StaticResource StoryboardNormal_FillBehavior_Stop}"/>
                </Trigger.ExitActions>
                <Trigger.EnterActions>
                    <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
                    <BeginStoryboard x:Name="StoryboardIsDisabled_BeginStoryboard" Storyboard="{StaticResource StoryboardIsDisabled}"/>
                </Trigger.EnterActions>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>



    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年2月9日 23:36

すべての返信

  • 複数のトリガーで同時に条件を満たした場合、後のAnimationで上書きされます。
    また、アニメーションはFillBehaviorによりアニメーションの最終状態を維持するか、停止するかが決まります。
    そのため、チェックボックスをUncheck->CheckするとIsEnabledのExitActionのアニメーションが実行されて、その影響が持続してしまいます。

    直すにはトリガーの順番を変えるか、FillBehavior=Stopに変えたトリガーを使います。

    <ControlTemplate.Triggers>
    
        <Trigger Property="IsEnabled" Value="False">
            <Trigger.ExitActions>
                <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
                <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
            </Trigger.ExitActions>
            <Trigger.EnterActions>
                <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
                <BeginStoryboard x:Name="StoryboardIsDisabled_BeginStoryboard" Storyboard="{StaticResource StoryboardIsDisabled}"/>
            </Trigger.EnterActions>
        </Trigger>
    
        <MultiTrigger>
            <MultiTrigger.ExitActions>
                <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
                <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
            </MultiTrigger.ExitActions>
            <MultiTrigger.EnterActions>
                <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
                <BeginStoryboard x:Name="StoryboardIsPressed_BeginStoryboard" Storyboard="{StaticResource StoryboardIsPressed}"/>
            </MultiTrigger.EnterActions>
            <MultiTrigger.Conditions>
                <Condition Property="IsEnabled" Value="True"/>
                <Condition Property="IsPressed" Value="True"/>
            </MultiTrigger.Conditions>
        </MultiTrigger>
    
    </ControlTemplate.Triggers>
    あるいは
    <ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
        <ControlTemplate.Resources>
            <Storyboard x:Key="StoryboardNormal">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Black"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
    
            <Storyboard x:Key="StoryboardNormal_FillBehavior_Stop">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border"
                                    FillBehavior="Stop"><!-- 最後まで行ったら終わりにする -->
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Black"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
    
            <Storyboard x:Key="StoryboardIsPressed">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF0017FF"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
            <Storyboard x:Key="StoryboardIsDisabled">
                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border" >
                    <EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF8B8B8B"/>
                </ColorAnimationUsingKeyFrames>
            </Storyboard>
        </ControlTemplate.Resources>
        <Border x:Name="border" Background="Black"/>
        <ControlTemplate.Triggers>
            <MultiTrigger>
                <MultiTrigger.ExitActions>
                    <!-- IsPressed -> Normal の状態遷移で実行する Storyboard -->
                    <BeginStoryboard Storyboard="{StaticResource StoryboardNormal}"/>
                </MultiTrigger.ExitActions>
                <MultiTrigger.EnterActions>
                    <!-- Normal -> IsPressed の状態遷移で実行する Storyboard -->
                    <BeginStoryboard x:Name="StoryboardIsPressed_BeginStoryboard" Storyboard="{StaticResource StoryboardIsPressed}"/>
                </MultiTrigger.EnterActions>
                <MultiTrigger.Conditions>
                    <Condition Property="IsEnabled" Value="True"/>
                    <Condition Property="IsPressed" Value="True"/>
                </MultiTrigger.Conditions>
            </MultiTrigger>
    
            <Trigger Property="IsEnabled" Value="False">
                <Trigger.ExitActions>
                    <!-- IsDisabled -> Normal の状態遷移で実行する Storyboard -->
                    <!-- FillBehavior="Stop"のAnimationを適用する -->
                    <BeginStoryboard Storyboard="{StaticResource StoryboardNormal_FillBehavior_Stop}"/>
                </Trigger.ExitActions>
                <Trigger.EnterActions>
                    <!-- Normal -> IsDisabled の状態遷移で実行する Storyboard -->
                    <BeginStoryboard x:Name="StoryboardIsDisabled_BeginStoryboard" Storyboard="{StaticResource StoryboardIsDisabled}"/>
                </Trigger.EnterActions>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>



    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年2月9日 23:36
  • >gekka 様

    ご返信いただきありがとうございます。

    トリガーを定義する順番も処理に影響するのですね。

    そのあたりの仕様がよくわかっていなかったので、大変助かりました。

    また、FillBehavior というものがあることも初めて知りました。

    今後はうまく使っていきたいと思います。

    お教えいただき本当にありがとうございました。

    2017年2月11日 12:30