WPF:自動的に下部に隠れるメニュー

組込みではいまだにサイズの小さいLCDなんて当たり前。QVGA(320×240)でもあれば相当立派なUIです。この場合、通常メニューを隠しておいてユーザー操作で表示するというのもよくある話。別画面にしてもいいけど、ユーザ的には面倒くさいし操作画面に対する直観性が失われます。そこで画面下部に隠しておけるメニューを作ってみました。

すべてをリソースとして定義します。

MainWindow.xaml – (1/3)

<Window x:Class="WpfAutoHideMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:hm="clr-namespace WpfAutoHideMenu"
        Title="MainWindow" Height="240" Width="320"/>

今回の名前空間は”hm”としています。続いて、メインとなる部分。定義するリソースは3つ!

MainWindow.xaml – (2/3)

    <Window.Resources>
        <ControlTemplate x:Key="BottomMenuList0" TargetType="ListBox">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="12"/>
                    <RowDefinition Height="12"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Border Grid.Row="1" Grid.Column="0" BorderThickness="5">
                    <Border.BorderBrush>
                        <SolidColorBrush Color="Blue"/>
                    </Border.BorderBrush>
                    <Grid Background="Blue">
                    </Grid>
                </Border>
                <Border Grid.RowSpan="2" Grid.Column="0" BorderThickness="5" CornerRadius="15">
                    <Border.BorderBrush>
                        <SolidColorBrush Color="Blue"/>
                    </Border.BorderBrush>
                    <Grid Background="Blue">
                        <TextBlock>タイトル</TextBlock>
                    </Grid>
                </Border>
                <Border Grid.Row="2" Grid.ColumnSpan="4" Opacity="0.7">
                    <Grid Background="Red">
                        <ItemsPresenter></ItemsPresenter>
                    </Grid>
                </Border>
            </Grid>
        </ControlTemplate>
        <ItemsPanelTemplate x:Key="AutoHideMenuItemsPanel">
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
        <Style x:Key="AutoShowHideStyle" TargetType="ListBox">
            <Style.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation To="80" Duration="0:0:0.25" Storyboard.TargetProperty="Height"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="MouseLeave">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation To="24" Duration="0:0:0.25" Storyboard.TargetProperty="Height"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

まずControlTemplateは、ListBoxとして表示するメニューの外観を決めるものです。3×3のGridを定義してタイトル表示部に2行、メインコンテンツ表示に1行を用意しています。複数のメニューを表示させる場合はタイトル表示列を可変にする仕組みを用意しておく必要がありますね(ここには含まれていません)。なぜGridが内部で使われているかですが、

その1 これと その2これを合体させて、

合体 こうしているだけです。

もっと複雑な図形を作る場合や透明度を設定する場合はPathなど使ってくださいませ。

次にItemsPanelTemplateは、ListBoxとして表示するコンテンツのレイアウトを決めるものです。ここももうちょっと凝ってもいいのですが、今回は単に横表示になればOKレベルなので、StackPanelでアイテムが水平方向に並ぶようにしています。

最後にStyleは、マウスが入ってきた/抜けていったことに対して下部メニュー表示させたり隠したりするためのトリガーを設定するものです。トリガーそのものをリソースに定義できないのでこのようにスタイルという形で定義しました。マウスが入ってくるとトリガーが発動して高さを規定量まで増やし、マウスが出ていくと別のトリガーが発動し高さを元の高さまで戻します。

これらを使うのは以下のような感じになります。

    <Grid>
        <ListBox Height ="24" VerticalAlignment="Bottom"
                 Template="{StaticResource BottomMenuList0}"
                 ItemsPanel="{StaticResource AutoHideMenuItemsPanel}"
                 Style="{StaticResource AutoShowHideStyle}">
            <Button>aaa</Button>
            <Button>bbb</Button>
            <Button>ccc</Button>
            <Button>ddd</Button>
            <Button>eee</Button>
        </ListBox>
    </Grid>

ここは簡単ですね!Template, ItemsTemplate, Styleをわざわざ定義しなくてもTargetTypeでそれぞれを割り当てることが可能ですが、 隠れたり表示したりする下部メニューのような動きがデフォルトにすることはないだろうと思って、リソース名割り当て参照しています。

これを使った画面はこちら。

マウスがない場合はタイトルのみ表示されていたメニューが、
通常状態

マウスが来ると中身が現れます!
マウスがきた
マウスが離れると、また上に戻ります。

このタイトル部を並べれば代わる代わる別のメニューを開けますね。でも注意してください、その場合はZオーダーを考える必要がでてきます。何もしなかったら”タイトル”表示部は他のメニューが拡大して表示している間にも表示されたままで見た目はかっちょ悪です。その場合は親パネルをGridでなく、ZIndexを操作するCanvasに変える必要があります。そうするとタイトルの配置方法もちょっと変わってきて、トリガーでは高さを変えてもダメで…といろいろ考える必要があります。

レイアウトってむずかしー

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中