WPFでボタンにホバーエフェクトをかける

f:id:Takachan:20171224200336p:plain

WFPのボタンコントロールにホバーエフェクトをかけたいと思います。

マウスオーバーするとボタンの色が変わります。完成すると以下のようなイメージになります。GIFにすると画質悪い…

f:id:Takachan:20171224195219g:plain

XAML

メイン画面のXAMLは以下の通りです。

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Width="200" Height="54" Background="Black" Style="{StaticResource SimpleBackgroundTransition}">
            <TextBlock FontSize="18" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White">Button</TextBlock>
        </Button>
    </Grid>
</Window>

スタイルの定義はボタンをフラット化する定義を継承してホバーエフェクトを作成します。

BackgroundはBlush型で、ColorAnimationはColor型なのでBackground.Colorでアニメーションを指定する。

FtomとToはとりあえず固定値じゃないとInvalidOperationExceptionが出る。(InvalidOperationException: スレッド間で使用するために、この Storyboard タイムライン ツリーを固定することはできません。というエラーが出る。)カスタムコントロール化すれば回避できそうですが、いちいちコントロール作ってられないと思う・・・

// Resource/ButtonAnimation.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp2">
    <Style x:Key="FlatButton" TargetType="Button" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
        <Setter Property="OverridesDefaultStyle" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Background="{TemplateBinding Background}">
                        <ContentPresenter x:Name="_Contents"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="SimpleBackgroundTransition" TargetType="Button" BasedOn="{StaticResource FlatButton}">
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
                <Trigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <!-- to and fron bvalue can not binding? -->
                            <ColorAnimation Storyboard.TargetProperty="Background.Color" 
                                            From="Black" To="#59b1eb" Duration="0:0:0.3" />
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <!-- to and fron bvalue can not binding? -->
                            <ColorAnimation Storyboard.TargetProperty="Background.Color" 
                                            From="#59b1eb" To="Black" Duration="0:0:0.3" />
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.ExitActions>
            </Trigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

そして、App.xamlで上記定義をアプリに取り込みます。

<Application x:Class="WpfApp2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp2"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Resource/ButtonAnimation.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

・・・でもこれ以上複雑な、たとえば子要素と連携したりするような動きを実現するのはちょっと難しそうですね。