SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

Blendを使ってButtonのホバーエフェクトを作る Part2

昨日の続きで、また別のホバーエフェクトを真似してみました。


今回はblendの操作手順とかはなしで、xamlコードだけメモしときます。
てか、添付プロパティとかを多用してると、BlendのUI上からアニメーションの設定とかできない。。。
(ContentPresenter内でのTextBlock.Foregroundプロパティの設定とか。。。)

背景・ボーダー・文字カラーをふわっと変更するボタン

        <ControlTemplate x:Key="ButtonControlTemplate2" TargetType="{x:Type Button}">
            <Border x:Name="border" Background="#FF333333" BorderBrush="#FF333333" BorderThickness="2">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal">
                            <Storyboard>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FF333333"/>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FF333333"/>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="contentPresenter">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FFFFFFFF"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="contentPresenter">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FF59B1EB"/>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="White"/>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.3" Value="#FF59B1EB"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Pressed">
                            <Storyboard>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="contentPresenter">
                                    <EasingColorKeyFrame KeyTime="0:0:0.2" Value="#FF59B1EB">
                                        <EasingColorKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingColorKeyFrame.EasingFunction>
                                    </EasingColorKeyFrame>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.2" Value="#FFCDCDCD">
                                        <EasingColorKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingColorKeyFrame.EasingFunction>
                                    </EasingColorKeyFrame>
                                </ColorAnimationUsingKeyFrames>
                                <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="border">
                                    <EasingColorKeyFrame KeyTime="0:0:0.2" Value="#FF59B1EB">
                                        <EasingColorKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingColorKeyFrame.EasingFunction>
                                    </EasingColorKeyFrame>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Disabled"/>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <ContentPresenter x:Name="contentPresenter" TextBlock.Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" OpacityMask="Black"/>
            </Border>
        </ControlTemplate>

文字間隔を広げるエフェクト

TextBlockの文字間隔を調整するプロパティはありません。
そこで、以下のサイトに書かれていた方法で、TextBlock内の文字間隔を調整しています。
テキストをItemsControlのItemsSourceに渡して、1文字ずつ描画する、という何ともトリッキーな方法です。
http://stackoverflow.com/questions/5843562/how-can-i-specify-letter-spacing-or-kerning-in-a-wpf-textbox


あと、DataTemplate内の要素のプロパティをStoryBoardから操作できなかったので、以下のような方法でマージンのアニメーションを行っています。

  1. 画面に表示しないダミーのTextBlockを作成
  2. DataTemplate内のTextBlockは、↑のダミーのTextBlockのMarginとバインド
  3. StoryBoardでは、このダミーのTextBlockのMargin値をアニメーションさせる。

これも随分とトリッキーなやり方なので、ここで書いた例はダメなサンプルですね。。。
ただ、「ダミーのオブジェクトを介して無理やりバインドを適用する」ってのは、いざという時のダーティーテクとして使えるかも。

マウスクリック時には、RenderTransformを使って、文字を少し下に移動させてます。

        <ControlTemplate x:Key="ButtonControlTemplate3" TargetType="{x:Type Button}">
            <Border Background="#FF333333">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal">
                            <Storyboard>
                                <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="txtDummyObject">
                                    <EasingThicknessKeyFrame KeyTime="0:0:0.3" Value="0">
                                        <EasingThicknessKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingThicknessKeyFrame.EasingFunction>
                                    </EasingThicknessKeyFrame>
                                </ThicknessAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="txtDummyObject">
                                    <EasingThicknessKeyFrame KeyTime="0:0:0.3" Value="3,0">
                                        <EasingThicknessKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingThicknessKeyFrame.EasingFunction>
                                    </EasingThicknessKeyFrame>
                                </ThicknessAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Pressed">
                            <Storyboard>
                                <ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="txtDummyObject">
                                    <EasingThicknessKeyFrame KeyTime="0:0:0.2" Value="3,0">
                                        <EasingThicknessKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingThicknessKeyFrame.EasingFunction>
                                    </EasingThicknessKeyFrame>
                                </ThicknessAnimationUsingKeyFrames>
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="txtDummyObject">
                                    <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="3">
                                        <EasingDoubleKeyFrame.EasingFunction>
                                            <CubicEase EasingMode="EaseInOut"/>
                                        </EasingDoubleKeyFrame.EasingFunction>
                                    </EasingDoubleKeyFrame>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Disabled"/>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <Grid>
                    <TextBlock x:Name="txtDummyObject" Visibility="Collapsed" Margin="0" RenderTransformOrigin="0.5,0.5">
                        <TextBlock.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform/>
                                <SkewTransform/>
                                <RotateTransform/>
                                <TranslateTransform/>
                            </TransformGroup>
                        </TextBlock.RenderTransform>
                    </TextBlock>
                    <ItemsControl ItemsSource="{Binding Content, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center"
                                  TextBlock.Foreground="White">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal" />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" 
                                           Margin="{Binding Margin, ElementName=txtDummyObject}"
                                           RenderTransform="{Binding RenderTransform, ElementName=txtDummyObject}"
                                           RenderTransformOrigin="0.5,0.5"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Grid>
            </Border>
        </ControlTemplate>

とりあえず、今日はここまで。