トップ回答者
ContextMenuをIsOpenで開くとコマンドが無効になってしまう

質問
-
ContextMenuをIsOpenで開いた時にコンテキストメニューのコマンドが無効になってしまいます。どのようにしたら有効にできますでしょうか。
- IsOpenを使用する理由は、作成しているソフトが右クリックを含めキーカスタマイズ可能なためです。
- RoutedCommandである理由は、ショートカット対応やMenuコントロールと共有しているためです。(サンプルではMenuを実装していません)
- 初期化時にFocusしている理由は、標準操作でコンテキストメニューを開いた場合でもコマンドが無効になってしまう現象を回避するためです。(参考サイト http://www.wpftutorial.net/RoutedCommandsInContextMenu.html)
以下、サンプルコードになります。
左クリックでIsOpenによるコンテキストメニューを開く操作になります。右クリックで開いた直後に左クリックで開くと問題ないように開けることが有りますが、1~2秒してから左クリックすると問題現象が再現されます。環境:Windows10 Pro 64bit / VisualStudio Community 2015 / .Net Framework 4.6 (x86)
<Window x:Class="ContextMenu2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ContextMenu2" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid x:Name="grid" Background="White" Focusable="True" MouseLeftButtonUp="grid_MouseLeftButtonUp"/> </Window>
using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace ContextMenu2 { public partial class MainWindow : Window { // Hello Command public readonly static RoutedCommand HelloCommand = new RoutedCommand("HelloCommand", typeof(MainWindow)); // constructor public MainWindow() { InitializeComponent(); // initialize Hello Command HelloCommand.InputGestures.Add(new KeyGesture(Key.G, ModifierKeys.Control)); this.CommandBindings.Add(new CommandBinding(HelloCommand, (s, e) => { MessageBox.Show("HELLO!"); }, (s, e) => e.CanExecute = true)); // create ContextMenu this.grid.ContextMenu = new ContextMenu(); this.grid.ContextMenu.Items.Add(new MenuItem() { Header = "Hello", Command = HelloCommand }); // Set the logical focus to the window this.Focus(); } // click private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { this.grid.ContextMenu.IsOpen = true; } } }
回答
-
例えば2つのGridのContextMenuプロパティに同じContextMenuインスタンスを割り当てた場合に、IsOpenではどちらのGridを対象にして開くのか判らなくなり、PlacementTargetがnullになります。
ContextMenuに対してCommandBindingsを登録するなら良いのですが、RoutedEventがPlacementTargetから遡れないのでWindowに登録したCommandBindingsの判定に到達できません。解決方法はTargetPlacementを設定すればよくなります。
private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { this.grid.ContextMenu.PlacementTarget = sender as UIElement ; this.grid.ContextMenu.IsOpen = true; }
たまに成功するのは前回状態が解除までのわずかなタイミングで残っているからでしょうね
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 編集済み gekkaMVP 2017年2月12日 8:46
- 回答の候補に設定 いわさ Tak1waMVP, Moderator 2017年2月12日 8:50
- 回答としてマーク neelabo 2017年2月12日 9:49
すべての返信
-
private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (!this.grid.ContextMenu.IsOpen) { e.Handled = true; var mouseRightClickEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Right) { RoutedEvent = Mouse.MouseUpEvent, Source = sender, }; InputManager.Current.ProcessInput(mouseRightClickEvent); } }
左クリックしたときに右クリックのイベントを発生させるとうまく動くようですね。
参考サイト: http://stackoverflow.com/questions/28377365/open-buttons-contextmenu-on-left-click
-
例えば2つのGridのContextMenuプロパティに同じContextMenuインスタンスを割り当てた場合に、IsOpenではどちらのGridを対象にして開くのか判らなくなり、PlacementTargetがnullになります。
ContextMenuに対してCommandBindingsを登録するなら良いのですが、RoutedEventがPlacementTargetから遡れないのでWindowに登録したCommandBindingsの判定に到達できません。解決方法はTargetPlacementを設定すればよくなります。
private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { this.grid.ContextMenu.PlacementTarget = sender as UIElement ; this.grid.ContextMenu.IsOpen = true; }
たまに成功するのは前回状態が解除までのわずかなタイミングで残っているからでしょうね
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 編集済み gekkaMVP 2017年2月12日 8:46
- 回答の候補に設定 いわさ Tak1waMVP, Moderator 2017年2月12日 8:50
- 回答としてマーク neelabo 2017年2月12日 9:49
-
私がアップしたコードでも gekka さんのコードでも、コンテキストメニューが表示された後、項目をダブルクリックすると次回以降、コンテキストメニューの項目が無効になってしまいますね。
そんな操作をすることはあまりないと思いますが、この現象はWPFが抱える問題なのか、このサンプルの問題なのか・・・
追伸:
this.CommandBindings.Add(new CommandBinding(HelloCommand, (s, e) =>{ MessageBox.Show("HELLO!"); grid.Focus(); // 追加 }, (s, e) => e.CanExecute = true));
メッセージボックス表示後にフォーカスを戻してやるとよいみたいですね。- 編集済み kenjinoteMVP 2017年2月12日 13:58