.NET Framework 4.5 を使い、以下のようなウィンドウを実装します。

<Window x:Class="ListViewContextMenuTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBlock Text="ABCDEFG&#x0a;HIJKLMN">
            <TextBlock.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="RoutedCommand" Command="ApplicationCommands.Help"/>
                    <MenuItem Header="PropertyCommand" Command="{Binding PropertyCommand}"/>
                </ContextMenu>
            </TextBlock.ContextMenu>
        </TextBlock>
        <Button Content="XYZ"/>
    </StackPanel>
</Window>
namespace ListViewContextMenuTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();

            CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, Command_Executed, Command_CanExecute));
        }

        private void Command_Executed(object sender, ExecutedRoutedEventArgs e)
        {
        }

        private void Command_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
    }

    public class MainViewModel
    {
        public class PropertyCommandClass : ICommand
        {
            public event EventHandler CanExecuteChanged;

            public void Execute(object parameter)
            {
            }

            public bool CanExecute(object parameter)
            {
                return true;
            }
        }

        public PropertyCommandClass PropertyCommand { get; private set; }

        public MainViewModel()
        {
            PropertyCommand = new PropertyCommandClass();
        }
    }
}

分離コードは単に CanExecute に応答しているだけです。

これを実行してすぐに TextBlock の上で右クリックすると、ルーティングコマンドを設定したメニューアイテムは enabled にならず、ビューモデルのプロパティにあるコマンドオブジェクトを設定したメニューアイテムだけが enabled になります。Console.WriteLine などでログを出すようにしてみると、ルーティングコマンドの方の CanExecute (Command_CanExecute) は呼ばれていないことが分かります。何度右クリックしても同じです。

一方、一度ボタンを押してからまた TextBlock の上で右クリックすると、今度は CanExecute が呼ばれ、ちゃんとメニューアイテムが enabled になります。一旦そうなるとずっと CanExecute の応答に従う状態のままです。(上のコードでは常に true ですが)

試したこと(全部効果なし)

  • MainWindow のコンストラクタの最後に CommandManager.InvalidateRequerySuggested を呼ぶ
  • TextBlock の OnContextMenuOpening イベントが発生した時に CommandManager.InvalidateRequerySuggested を呼ぶ
  • CommandBindings.Add を使わずに XAML で記述する

こちらとしては最初から CanExecute が呼ばれることを期待しているのですが、何がいけないんでしょう…

Trackback

no comment untill now

Add your comment now