none
ContextMenuをIsOpenで開くとコマンドが無効になってしまう RRS feed

  • 質問

  • 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;
            }
        }
    }


    2017年2月12日 7:22

回答

  • 例えば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!)

    2017年2月12日 8:44

すべての返信

  • 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

    2017年2月12日 8:08
  • 例えば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!)

    2017年2月12日 8:44
  • ご回答ありがとうございます。

    こちらの対処で期待する動作になりました!

    2017年2月12日 9:57
  • ご回答ありがとうございます。

    こちらの方法でもうまくいきそうですね。

    2017年2月12日 10:03
  • 私がアップしたコードでも gekka さんのコードでも、コンテキストメニューが表示された後、項目をダブルクリックすると次回以降、コンテキストメニューの項目が無効になってしまいますね。

    そんな操作をすることはあまりないと思いますが、この現象はWPFが抱える問題なのか、このサンプルの問題なのか・・・

    追伸:

    this.CommandBindings.Add(new CommandBinding(HelloCommand, (s, e) =>{
        MessageBox.Show("HELLO!");
        grid.Focus(); // 追加
        }, (s, e) => e.CanExecute = true));
    
    メッセージボックス表示後にフォーカスを戻してやるとよいみたいですね。
    2017年2月12日 10:36