ContextMenuはコントロールのMouseRightButtonUpイベントによって表示されます。
これはルーティングイベントと言われるもので、親要素から子要素にイベントが下っていくトンネルイベントと子要素から親要素に上がっていくバブルイベントの2種類があり(本当は直接ルーティングもあるけど割愛)、WPFは各種イベントに該当する操作をキャッチするとトンネル→バブルの順で対応するコントロールのイベントをハンドルします。
例として今回の場合はまずマウスの右ボタンが離れると、トンネルイベントであるPreviewMouseRightButtonUpがGrid→Expander→TreeView→TreeViewItemの順でハンドルされます。その後、バブルイベントであるMouseRightButtonUpがTreeViewItem→TreeView→Expander→Gridの順でハンドルされます。
Expanderクリック時にGridのContextMenuが表示されるのはExpanderにはMouseRightButtonUpで特に何かをするように設定されていないので、素通りしてそのままGridまでMouseRightButtonUpイベントが上がっているからです。
ExpanderにGridのContextMenuを表示させない方法としては幾つかあります。
①Expanderには表示させたくないContextMenuを表示させたい領域にGridを設定して、ExpanderをGridの子要素から外す。基本的にはこれが一般的。
XAML
1<Grid>
2 <Grid.RowDefinitions>
3 <RowDefinition/>
4 <RowDefinition/>
5 </Grid.RowDefinitions>
6 <!-- このGridにはContextMenuが表示される-->
7 <Grid
8 Grid.Row="0"
9 Background="Transparent">
10 <Grid.ContextMenu>
11 <ContextMenu>
12 <MenuItem Header="Grid1..."/>
13 <MenuItem Header="Grid2..."/>
14 <MenuItem Header="Grid3..."/>
15 </ContextMenu>
16 </Grid.ContextMenu>
17 </Grid>
18 <!-- ContextMenuを設定したGridの子要素ではないので表示されない-->
19 <Expander
20 Grid.Row="1"
21 Header="expander">
22 <TreeView
23 IsHitTestVisible="True">
24 <TreeView.Resources>
25 <ContextMenu x:Key="ItemPopup1">
26 <MenuItem Header="New Scale..."/>
27 </ContextMenu>
28 <ContextMenu x:Key="ItemPopup2">
29 <MenuItem Header="Remove Scale"/>
30 </ContextMenu>
31 </TreeView.Resources>
32
33 <!-- TreeViewItemにはContexMenuを表示!-->
34 <TreeViewItem Header="first" ContextMenu="{StaticResource ItemPopup1}" />
35 <TreeViewItem Header="second" ContextMenu="{StaticResource ItemPopup2}" />
36 </TreeView>
37 </Expander>
38</Grid>
ExpanderをどうしてもGridの子要素から外せない場合は、
②ExpanderのMouseRightButtonUpにイベントを設定してGridにMouseRightButtonUpイベントが行かないようにする。
XAML
1<Grid>
2 <!-- GridにはContexMenuを表示! -->
3 <Grid.ContextMenu>
4 <ContextMenu>
5 <MenuItem Header="Grid1..."/>
6 <MenuItem Header="Grid2..."/>
7 <MenuItem Header="Grid3..."/>
8 </ContextMenu>
9 </Grid.ContextMenu>
10
11 <!-- ContextMenuを表示させたくない! -->
12 <Expander
13 MouseRightButtonUp="Expander_MouseRightButtonDown"
14 Header="expander">
15 <TreeView>
16 <TreeView.Resources>
17 <ContextMenu x:Key="ItemPopup1">
18 <MenuItem Header="New Scale..."/>
19 </ContextMenu>
20 <ContextMenu x:Key="ItemPopup2">
21 <MenuItem Header="Remove Scale"/>
22 </ContextMenu>
23 </TreeView.Resources>
24
25 <!-- TreeViewItemにはContexMenuを表示! -->
26 <TreeViewItem Header="first" ContextMenu="{StaticResource ItemPopup1}" />
27 <TreeViewItem Header="second" ContextMenu="{StaticResource ItemPopup2}" />
28 </TreeView>
29 </Expander>
30</Grid>
コードビハインド
C#
1private void Expander_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
2{
3 e.Handled = true;
4}
このe.Handledにtrueを入れるとそのイベントを設定したコントロール以降のコントロールにイベントがルーティングされません。トンネルイベントなら全ての子要素、バブルイベントなら全ての親要素の該当イベントを握りつぶします。TreeViewItemを右クリックした時にGridのContextMenuが表示されずにTreeViewItemのContextMenuのみ表示されるのはTreeViewItemがMouseRightButtonUpイベントを握りつぶしているからです。
コードビハインドなんて使いたくないというのであれば一番簡単なのは、
③Expanderに空のContextMenuを入れる。
XAML
1<Grid>
2 <!-- GridにはContexMenuを表示! -->
3 <Grid.ContextMenu>
4 <ContextMenu>
5 <MenuItem Header="Grid1..."/>
6 <MenuItem Header="Grid2..."/>
7 <MenuItem Header="Grid3..."/>
8 </ContextMenu>
9 </Grid.ContextMenu>
10
11 <!-- ContextMenuを表示させたくない! -->
12 <Expander
13 Header="expander">
14 <Expander.ContextMenu>
15 <ContextMenu/>
16 </Expander.ContextMenu>
17 <TreeView>
18 <TreeView.Resources>
19 <ContextMenu x:Key="ItemPopup1">
20 <MenuItem Header="New Scale..."/>
21 </ContextMenu>
22 <ContextMenu x:Key="ItemPopup2">
23 <MenuItem Header="Remove Scale"/>
24 </ContextMenu>
25 </TreeView.Resources>
26
27 <!-- TreeViewItemにはContexMenuを表示! -->
28 <TreeViewItem Header="first" ContextMenu="{StaticResource ItemPopup1}" />
29 <TreeViewItem Header="second" ContextMenu="{StaticResource ItemPopup2}" />
30 </TreeView>
31 </Expander>
32</Grid>
私が思いつくのはこれくらい。
これ以外だとExpanderのTemplateいじるとかカスタムコントロール作ってHitTestのロジックいじるとかめんどくさいことくらいしか思いつかないからここでは書きません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/18 06:31