[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;
}
}
}
}
おしまい。





ディスカッション
コメント一覧
まだ、コメントがありません