通常のリボンコントロール代替案は「Fluent.Ribbon」になるのですが、コントロールが Office っぽく寄せすぎてしまうため、とりあえずのリボンコントロールを TabControl
で作ってみた一例の記事です。
こんな感じで、エクスプローラーくらいの見た目のリボンコントロールなら、それっぽく作ることができます。
使い方のコード
リボンメニューを作るときにポイントになるのは、左端の「ファイル」ボタンです。今回は ContentPresenter
で対応してみました。RibbonTab
(作成したコントロール)に用意したプロパティ Menu
が対応しています。
Menu
には System.Windows.Controls.Menu を設定してみると、こんな感じに。
Menu
コントロールは、高さが自由ではないみたいです。固定値に設定すると子要素MenuItem
は縦方向の Center がうまく機能しなかったです。そこで仕方なく Width と Height を親要素にバインディングしてサイズを設定しています。(コントロールをテンプレートで作り直したほうがいいかも)
<local:RibbonTab> <local:RibbonTab.Menu> <Menu Background="{StaticResource RibbonMenu_DefaultColorKey}" Foreground="White" IsMainMenu="True" Width="60"> <MenuItem Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Menu}}}" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Menu}}}" HorizontalContentAlignment="Center" Header="ファイル"> <MenuItem Header="A" /> <MenuItem Header="A" /> </MenuItem> </Menu> </local:RibbonTab.Menu> <!-- タブの要素 --> <TabItem Header="ホーム"> <StackPanel Orientation="Horizontal" MinHeight="112"> <!-- 要素1 --> <DockPanel Width="100"> <Button DockPanel.Dock="Top" Margin="0 20 0 0" Template="{StaticResource RibbonButtonTemplate}" HorizontalAlignment="Center"> <Button.Content> <Border BorderThickness="1" BorderBrush="#FFCCCCCC" Padding="2"> <Image Width="40" Height="40" /> </Border> </Button.Content> </Button> <TextBlock DockPanel.Dock="Bottom" Margin="0 10" HorizontalAlignment="Center" VerticalAlignment="Bottom" Text="サンプル" Foreground="#FFCCCCCC" /> </DockPanel> <!-- 縦線 --> <Border Margin="0 6" BorderThickness="1 0 0 0" BorderBrush="#FFCCCCCC" /> </StackPanel> </TabItem> <TabItem Header="共有"> <StackPanel Orientation="Horizontal" MinHeight="112"> </StackPanel> </TabItem> <TabItem Header="表示"> <StackPanel Orientation="Horizontal" MinHeight="112"> </StackPanel> </TabItem> <TabItem Header="クリップボード"> <StackPanel Orientation="Horizontal" MinHeight="112"> </StackPanel> </TabItem> </local:RibbonTab>
リボン TabControl のコード
ポイントは TargetType で TabControl
ではなくて自分で作成する TabControl
を継承したクラス名を指定しているところ。
新しく追加した DependencyProperty
の Menu
を参照するためには、TabControl
を対象にしていてもプロパティの存在がわからない。
<Style TargetType="{x:Type local:RibbonTab}"> <Setter Property="BorderThickness" Value="1 1 1 1" /> <Setter Property="BorderBrush" Value="{StaticResource RibbonBorderColorKey}" /> <Setter Property="Padding" Value="0" /> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:RibbonTab}"> <Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="0" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Grid.Column="0" Grid.Row="0" Orientation="Horizontal"> <!-- ファイルボタン --> <ContentPresenter Content="{TemplateBinding Menu}" RecognizesAccessKey="True" Margin="0 0 0 0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> <!-- 各タブボタン --> <TabPanel IsItemsHost="true" Margin="2 2 2 0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </StackPanel> <Border Grid.Column="0" Grid.Row="1" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local" Panel.ZIndex="-1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" > <!-- 各タブの内容 --> <ContentPresenter ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type TabItem}"> <Setter Property="FocusVisualStyle" Value="{x:Null}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TabItem}"> <Border x:Name="ItemHeader" BorderThickness="1 1 1 0" Background="White" Margin="-2 -2 0 -1"> <Border x:Name="ItemHeaderUnderBorder" BorderThickness="0 0 0 1" Margin="1 0 0 0"> <ContentPresenter x:Name="ContentPresenter" Margin="5 5 6 6" MinWidth="60" Height="18" HorizontalAlignment="Center" VerticalAlignment="Center" ContentSource="Header" > <ContentPresenter.Resources> <Style TargetType="{x:Type TextBlock}"> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" /> </Style> </ContentPresenter.Resources> </ContentPresenter> </Border> </Border> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="ItemHeader" Property="BorderThickness" Value="1 1 1 0" /> <Setter TargetName="ItemHeader" Property="BorderBrush" Value="{StaticResource RibbonBorderColorKey}" /> <Setter TargetName="ItemHeaderUnderBorder" Property="BorderBrush" Value="White" /> <Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{StaticResource RibbonTabItem_SelectedFontColorKey}" /> </Trigger> <Trigger Property="IsSelected" Value="False"> <Setter TargetName="ItemHeader" Property="BorderBrush" Value="Transparent" /> <Setter TargetName="ItemHeader" Property="Background" Value="Transparent" /> <Setter TargetName="ItemHeaderUnderBorder" Property="BorderBrush" Value="{StaticResource RibbonBorderColorKey}" /> <Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{StaticResource RibbonTabItem_FontColorKey}" /> </Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" SourceName="ItemHeader" Value="True" /> <Condition Property="IsSelected" Value="False" /> </MultiTrigger.Conditions> <Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{StaticResource RibbonTabItem_SelectedFontColorKey}" /> </MultiTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
プロパティを追加したところ。
public partial class RibbonTab : TabControl { #region DependencyProperties public static readonly DependencyProperty MenuProperty = DependencyProperty.Register ( nameof(Menu), typeof(object), typeof(RibbonTab), new FrameworkPropertyMetadata(null) ); /// <summary> /// 「ファイル」メニューを設定するプロパティを取得または設定します。 /// </summary> [System.ComponentModel.Bindable(true)] public object Menu { get { return GetValue(MenuProperty); } set { SetValue(MenuProperty, value); } } #endregion public RibbonTab() { InitializeComponent(); } }
補足
WPF では、リボンコントロールを利用するためにかつて「Microsoft.Windows.Controls.Ribbon」というものが用意されたのですが、枠の太さが変わったり角が丸くなったりと、制御できないおかしな挙動をします。下記のタイトルのように、私もおすすめしません。
枠が細くなる問題は、Visual Studio のフェードバックにも出ていたと思いますが、コントロールは十分な修正がないままのようです。1
というか、コントロールデザイン自体が(リボンなのに)スッキリしておらず、古くなってしまっていると思います。
その他のトラブル
Menu
と MenuItem
が左に開く、伸びていくことがあったら、レジストリーを修正して、再起動。
「1」だと問題があって、「0」に修正してください。(レジストリーなのでPC の再起動必須です)
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows
WACOM のペンタブなんかを利用していると発生してしまう別問題のようです。
サンプル
GitHub の「Samples」リポジトリーにまとめて公開しています。今回のサンプルは「RibbonMenuSample」です。