キャプション付きの画像表示を行うサンプル
マウスオーバー時に、アニメーションしながらキャプション表示を行うControlTemplateを作ってみました。
仕組み
ContentControlのControlTemplateとして作ってるので、画像表示に限らず任意のコントロールに対してキャプション表示を行えます。(そんな用途あるのか、、、って感じですが。)
また、キャプション表示用の文字列は、ContentControlのTagプロパティを使って表示します。
キャプション表示時のアニメーションは、MouseEnterイベントとMouseLeaveイベントをトリガーとして、キャプションの表示/非表示を切り替えるアニメーションのStoryboardを動かしています。
キャプション表示を行うContentControl
画像の準備
以下のようにContentTemplateを適用したContentControlの内部に、Imageコントロールを配置しておきます。
<ContentControl Width="300" Height="200" Margin="20" HorizontalAlignment="Left" VerticalAlignment="Top" Tag="キャプションのテスト" Template="{DynamicResource CaptionImageControlTemplate1}"> <Image HorizontalAlignment="Center" VerticalAlignment="Center" RenderOptions.BitmapScalingMode="HighQuality" Source="Images/9.jpg" Stretch="UniformToFill" /> </ContentControl>
キャプションをふわっと表示する
以下のようなControlTemplateを前述のContentControlに適用すると、こんなアニメーションでキャプション表示できます。
<ControlTemplate x:Key="CaptionImageControlTemplate1" TargetType="{x:Type ContentControl}"> <ControlTemplate.Resources> <Storyboard x:Key="OnMouseEnter"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(UIElement.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1" /> </DoubleAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="OnMouseLeave"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(UIElement.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </ControlTemplate.Resources> <Grid> <ContentPresenter /> <TextBlock x:Name="textBlock" VerticalAlignment="Bottom" Background="#88000000" Foreground="White" Opacity="0" Padding="5" Text="{TemplateBinding Tag}" /> </Grid> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <BeginStoryboard Storyboard="{StaticResource OnMouseLeave}" /> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <BeginStoryboard Storyboard="{StaticResource OnMouseEnter}" /> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate>
キャプションを下部からスライドインさせる
次はこんな感じ
<ControlTemplate x:Key="CaptionImageControlTemplate2" TargetType="{x:Type ContentControl}"> <ControlTemplate.Resources> <Storyboard x:Key="OnMouseEnter"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="OnMouseLeave"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="25"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </ControlTemplate.Resources> <Grid ClipToBounds="True"> <ContentPresenter /> <TextBlock x:Name="textBlock" VerticalAlignment="Bottom" Background="#88000000" Foreground="White" Padding="5" RenderTransformOrigin="0.5,0.5" Text="{TemplateBinding Tag}"> <TextBlock.RenderTransform> <TransformGroup> <ScaleTransform /> <SkewTransform /> <RotateTransform /> <TranslateTransform Y="25" /> </TransformGroup> </TextBlock.RenderTransform> </TextBlock> </Grid> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <BeginStoryboard Storyboard="{StaticResource OnMouseLeave}" /> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <BeginStoryboard Storyboard="{StaticResource OnMouseEnter}" /> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate>
回転しながらキャプション表示を行う
↓の記事で紹介されてたものも真似してみました。
http://coliss.com/articles/build-websites/operation/css/css-incontent.html
<ControlTemplate x:Key="CaptionImageControlTemplate3" TargetType="{x:Type ContentControl}"> <ControlTemplate.Resources> <Storyboard x:Key="OnMouseEnter"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="360"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="OnMouseLeave"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border" Storyboard.TargetProperty="(UIElement.Opacity)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1.1"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="contentPresenter" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1.1"> <EasingDoubleKeyFrame.EasingFunction> <QuarticEase EasingMode="EaseInOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </ControlTemplate.Resources> <Grid ClipToBounds="True"> <Viewbox HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="UniformToFill"> <ContentPresenter x:Name="contentPresenter" RenderTransformOrigin="0.5,0.5"> <ContentPresenter.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="1.1" ScaleY="1.1" /> <SkewTransform /> <RotateTransform /> <TranslateTransform /> </TransformGroup> </ContentPresenter.RenderTransform> </ContentPresenter> </Viewbox> <Border x:Name="border" Background="#88000000" RenderTransformOrigin="0.5,0.5"> <Border.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="0" ScaleY="0" /> <SkewTransform /> <RotateTransform /> <TranslateTransform /> </TransformGroup> </Border.RenderTransform> <TextBlock x:Name="textBlock" HorizontalAlignment="Center" VerticalAlignment="Top" Foreground="White" Padding="5" Text="{TemplateBinding Tag}" /> </Border> </Grid> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <BeginStoryboard Storyboard="{StaticResource OnMouseLeave}" /> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <BeginStoryboard Storyboard="{StaticResource OnMouseEnter}" /> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate>
まとめ
前述したすべてのアニメーションで、画像ギャラリーみたいなのを作るとこんな感じ。
その他作ってた時のメモ
最初は、UserControlとして作ろうかと考えてました。ImageSourceとキャプション用のstringの依存関係プロパティを持ったユーザーコントロールとして。
でも、よくよく考えたら、UserControlにするまでもなく、ContorolTemplateのカスタマイズで対応できそうだったので、書き換えました。
あと、ImageコントロールはTemplateプロパティを持っていないので、ControlTemplateの設定ができず、ContentControlに対するテンプレートを作成しました。
この方が、いろんなコントロールに対して汎用的に使えるし。
あと、最初はVisualStateManagerを使ってホバー時の画面表示を定義しようとしてました。
でも、VisualStateを自分で定義したら、VisualStateの変更をXAML上で制御しようとした時に、GoToStateActionを使わなければならず、Blend SDKのdllへの参照が必要になってしまいます。
それでも、別にいいっちゃいいけど、何となくBlendのSDK抜きでできる方法を使いたくて、イベントトリガーを使って実装しました。