yotiky Tech Blog

とあるエンジニアの備忘録

WPF - MahApps の覚書

目次

前提

  • MahApps.Metro
  • MahApps.Metro.IconPacks

App.xaml でリソースを読み込む。

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Cyan.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

各コントロール名前空間を参照。

xmlns:metro="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:icon="http://metro.mahapps.com/winfx/xaml/iconpacks"

WindowCommands

<metro:MetroWindow.LeftWindowCommands>
    <metro:WindowCommands>
        <Button ToolTip="GitHub" Command="{Binding GitHubCommand}">
            <icon:PackIconEvaIcons Kind="Github"/>
        </Button>
    </metro:WindowCommands>
</metro:MetroWindow.LeftWindowCommands>
<metro:MetroWindow.RightWindowCommands>
    <metro:WindowCommands>
        <Button ToolTip="設定" Command="{Binding SettingsCommand}">
            <StackPanel Orientation="Horizontal">
                <icon:PackIconModern Width="22" Height="22" Kind="Settings" />
                <TextBlock Text="設定" VerticalAlignment="Center" Margin="5,0"/>
            </StackPanel>
        </Button>
    </metro:WindowCommands>
</metro:MetroWindow.RightWindowCommands>

f:id:yotiky:20201109214912p:plain

ListBox

<ListBox Height="180" Width="120"
    metro:MultiSelectorHelper.SelectedItems="{Binding SelectedItems}"
    metro:ScrollViewerHelper.EndOfVerticalScrollReachedCommand="{Binding EndOfScrollReachedCmdWithParameter, Mode=OneWay}"
    BorderThickness="1"
    DisplayMemberPath="Name"
    ItemsSource="{Binding Items}"
    SelectionMode="Multiple"
    Style="{StaticResource MahApps.Styles.ListBox.Virtualized}" />
public class SampleViewModel 
{
    public ObservableCollection<SampleItemViewModel> Items { get; } = new ObservableCollection<SampleItemViewModel>();
    public ObservableCollection<SampleItemViewModel> SelectedItems { get; } = new ObservableCollection<SampleItemViewModel>();
}

public class SampleItemViewModel 
{
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}

HamburgerMenu

Item のテンプレート。(公式サイトより)

HamburgerMenuGlyphItem は画像、HamburgerMenuIconItem はアイコンを表示する。画像はうまく表示できなかったのでスルー。

<Window.Resources>
    <!--  This is the template for all menu items. In this sample we use the glyph items.  -->
    <DataTemplate x:Key="HamburgerMenuGlyphItem" DataType="{x:Type metro:HamburgerMenuGlyphItem}">
        <DockPanel Height="48" LastChildFill="True">
            <Grid x:Name="IconPart"
                      Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type metro:HamburgerMenu}}, Path=CompactPaneLength}"
                      DockPanel.Dock="Left">
                <Image Margin="12"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Source="{Binding Glyph}" />
            </Grid>
            <TextBlock x:Name="TextPart"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
        </DockPanel>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type metro:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
                <Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
                <Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    
    <!--  This is the template for the option menu item  -->
    <DataTemplate x:Key="HamburgerMenuIconItem" DataType="{x:Type metro:HamburgerMenuIconItem}">
        <DockPanel Height="48" LastChildFill="True">
            <ContentControl x:Name="IconPart"
                                Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type metro:HamburgerMenu}}, Path=CompactPaneLength}"
                                Content="{Binding Icon}"
                                DockPanel.Dock="Left"
                                Focusable="False"
                                IsTabStop="False" />
            <TextBlock x:Name="TextPart"
                           VerticalAlignment="Center"
                           FontSize="16"
                           Text="{Binding Label}" />
        </DockPanel>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type metro:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
                <Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
                <Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</Window.Resources>
<metro:HamburgerMenu x:Name="HamburgerMenuControl"
                    DisplayMode="CompactOverlay"
                    HamburgerWidth="48"
                    IsPaneOpen="{Binding IsHamburgerMenuPaneOpen}"
                    ItemTemplate="{StaticResource HamburgerMenuIconItem}"
                    OptionsItemTemplate="{StaticResource HamburgerMenuIconItem}"
                    SelectedIndex="0"
                    OpenPaneLength="180"
                    VerticalScrollBarOnLeftSide="False">

    <!--  Items  -->
    <metro:HamburgerMenu.ItemsSource>
        <metro:HamburgerMenuItemCollection>
            <metro:HamburgerMenuIconItem Label="最新情報">
                <metro:HamburgerMenuIconItem.Icon>
                    <icon:PackIconJamIcons Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="BellF" />
                </metro:HamburgerMenuIconItem.Icon>
            </metro:HamburgerMenuIconItem>
            <metro:HamburgerMenuIconItem Label="チャット">
                <metro:HamburgerMenuIconItem.Icon>
                    <icon:PackIconMaterialDesign Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="Chat" />
                </metro:HamburgerMenuIconItem.Icon>
            </metro:HamburgerMenuIconItem>
            <metro:HamburgerMenuIconItem Label="チーム">
                <metro:HamburgerMenuIconItem.Icon>
                    <icon:PackIconPicolIcons Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="GroupHalf" />
                </metro:HamburgerMenuIconItem.Icon>
            </metro:HamburgerMenuIconItem>
            <metro:HamburgerMenuIconItem Label="予定表">
                <metro:HamburgerMenuIconItem.Icon>
                    <icon:PackIconBoxIcons Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="SolidCalendar" />
                </metro:HamburgerMenuIconItem.Icon>
            </metro:HamburgerMenuIconItem>
        </metro:HamburgerMenuItemCollection>
    </metro:HamburgerMenu.ItemsSource>

    <!--  Options  -->
    <metro:HamburgerMenu.OptionsItemsSource>
        <metro:HamburgerMenuItemCollection>
            <metro:HamburgerMenuIconItem Label="ヘルプ">
                <metro:HamburgerMenuIconItem.Icon>
                    <icon:PackIconEntypo Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="Help" />
                </metro:HamburgerMenuIconItem.Icon>
            </metro:HamburgerMenuIconItem>
        </metro:HamburgerMenuItemCollection>
    </metro:HamburgerMenu.OptionsItemsSource>

    <!--  Content  -->
    <metro:HamburgerMenu.Content>
        <StackPanel>
            <TextBlock Text="Hamburger Menu Sample" FontSize="24" Margin="10,0,0,100"/>
            <Button Content="Button" Width="120" Height="40"/>
            <Button Content="Button" Width="120" Height="40"/>
        </StackPanel>
    </metro:HamburgerMenu.Content>
</metro:HamburgerMenu>

Items は上部、Options は下部に配置される。 タブのようにクリックすると選択状態になる。

f:id:yotiky:20201110113417g:plain

参考

elf-mission.net

ボタンとして使用する

タブではなくボタンとして使用するには、選択状態にならないようする。

<metro:HamburgerMenu x:Name="HamburgerMenuControl"
                    DisplayMode="CompactOverlay"
                    HamburgerWidth="48"
                    IsPaneOpen="{Binding IsHamburgerMenuPaneOpen}"
                    ItemTemplate="{StaticResource HamburgerMenuIconItem}"
                    OptionsItemTemplate="{StaticResource HamburgerMenuIconItem}"
                    SelectedIndex="-1"
                    OpenPaneLength="180"
                    VerticalScrollBarOnLeftSide="False"
                    ItemClick="HamburgerMenuControl_ItemClick"
                    OptionsItemClick="HamburgerMenuControl_ItemClick">
private void HamburgerMenuControl_ItemClick(object sender, MahApps.Metro.Controls.ItemClickEventArgs args)
{
    HamburgerMenuControl.SelectedIndex = -1;
    HamburgerMenuControl.SelectedOptionsIndex = -1;
}

f:id:yotiky:20201110120314g:plain

ToolTipを表示する

閉じている時だけ表示する場合。

<DataTemplate x:Key="HamburgerMenuIconItem" DataType="{x:Type metro:HamburgerMenuIconItem}">
    <DockPanel x:Name="RootDock" Height="48" LastChildFill="True">
        <!-- 省略 -->
    </DockPanel>
    <DataTemplate.Triggers>
        <!-- 省略 -->
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type metro:HamburgerMenu}}, Path=IsPaneOpen}" Value="False">
            <Setter TargetName="RootDock" Property="ToolTip" Value="{Binding ToolTip, Mode=OneWay}" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>
<metro:HamburgerMenuIconItem Label="最新情報" ToolTip="最新情報をお届けします">

f:id:yotiky:20201110122958p:plain

常に表示する場合。

    <DockPanel Height="48" LastChildFill="True" ToolTip="{Binding ToolTip}">