[C# WPF] ContextMenuのCheckBoxをThreeStateで表示する
ContextMenuに3つの状態を遷移するチェックボックスを付けたくなりました。
標準のMenuItemはIsThreeStateを使用できなかったので、それっぽい動きをするものを自作しました。
CheckBoxと同様に、ContextMenuにもIsThreeStateを付けてくれればこんなことをしなくてもいいのですが。
具体的には、MenuItemを継承して状態フラグを追加したMenuItemを作っておいて、それに表示用のStyleをくっつけるだけです。
完成図
こんな感じに3パターンでチェックボックスを表示できます。
MenuItemを継承
Styleから参照できるように依存関係プロパティを追加するだけ。
ここではフラグをbool!で宣言しています。
///チェックボックスで3つの状態を表示できるMenuItem public class ThreeStateMenuItem : MenuItem { ///MenuItemの状態の依存関係プロパテ意 public static readonly DependencyProperty ThreeStateProperty = DependencyProperty.Register("ThreeState", typeof(bool?), typeof(ThreeStateMenuItem), new PropertyMetadata(false)); ///MenuItemの状態のCLRプロパティ public bool? ThreeState { get { return (bool?)GetValue(ThreeStateProperty); } set { SetValue(ThreeStateProperty, value); } } }
ThreeStateMenuItem用のスタイルを作成
3つの状態を表示するためのスタイルを作ります。
状態フラグをbool!型で宣言しているので、falseのときには非表示、trueのときにはチェック、nullのときにはハイフン、ということにしてあります。
<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ThreeStateMenuItem}"> <Border BorderThickness="1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="13"/> <ColumnDefinition Width="20"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Canvas Grid.Column="0"> <!-- Check Mark --> <Path x:Name="PART_CheckMark" Width="9" Height="9" Margin="6,4,0,0" Visibility="Collapsed" IsHitTestVisible="False" SnapsToDevicePixels="False" Stroke="Green" StrokeThickness="2" Data="M 0 4 L 3 7 M 3 9 L 9 0" /> <!-- Indeterminate Mark --> <Path x:Name="PART_IndeterminateMark" Width="7" Height="7" Margin="8,5,0,0" Visibility="Collapsed" IsHitTestVisible="False" SnapsToDevicePixels="False" Stroke="Green" StrokeThickness="2" Data="M 0 4 L 7 4" /> </Canvas> <ContentPresenter Grid.Column="2" ContentSource="Header" HorizontalAlignment="Left" VerticalAlignment="Center" /> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="ThreeState" Value="True"> <Setter TargetName="PART_CheckMark" Property="Visibility" Value="Visible"/> </Trigger> <Trigger Property="ThreeState" Value="{x:Null}"> <Setter TargetName="PART_IndeterminateMark" Property="Visibility" Value="Visible"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter>
動作を確認するために状態を切り替える処理を作る
こっちは主題ではないので適当にコンテキストメニューを開くたびに状態をひとつずつずらしていくようにしました。
まずはXaml。
<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:ThreeStateMenuItem}"> <Border BorderThickness="1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="13"/> <ColumnDefinition Width="20"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Canvas Grid.Column="0"> <!-- Check Mark --> <Path x:Name="PART_CheckMark" Width="9" Height="9" Margin="6,4,0,0" Visibility="Collapsed" IsHitTestVisible="False" SnapsToDevicePixels="False" Stroke="Green" StrokeThickness="2" Data="M 0 4 L 3 7 M 3 9 L 9 0" /> <!-- Indeterminate Mark --> <Path x:Name="PART_IndeterminateMark" Width="7" Height="7" Margin="8,5,0,0" Visibility="Collapsed" IsHitTestVisible="False" SnapsToDevicePixels="False" Stroke="Green" StrokeThickness="2" Data="M 0 4 L 7 4" /> </Canvas> <ContentPresenter Grid.Column="2" ContentSource="Header" HorizontalAlignment="Left" VerticalAlignment="Center" /> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="ThreeState" Value="True"> <Setter TargetName="PART_CheckMark" Property="Visibility" Value="Visible"/> </Trigger> <Trigger Property="ThreeState" Value="{x:Null}"> <Setter TargetName="PART_IndeterminateMark" Property="Visibility" Value="Visible"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter>
コード。
/// /// MainWindow.xaml の相互作用ロジック /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } ///コンテキストメニューを開いた際に呼び出されるイベント private void WindowContextMenu_Opened(object sender, RoutedEventArgs e) { var menu = sender as ContextMenu; if (menu == null) { return; } foreach (ThreeStateMenuItem item in menu.Items) { // メニューを開くたびに状態をひとつ遷移する if (item.ThreeState == true) { item.ThreeState = false; } else if (item.ThreeState == false) { item.ThreeState = null; } else { item.ThreeState = true; } } } }
おしまい。
ディスカッション
コメント一覧
まだ、コメントがありません