Пользовательские команды WPF в контекстном меню отключены до нажатия любой кнопки - PullRequest
17 голосов
/ 13 октября 2010

У меня есть пользовательская команда, и я пытаюсь выполнить ее из контекстного меню, но они всегда отображаются как отключенные, если я не нажму любую кнопку в пользовательском интерфейсе (кнопки не имеют никакого отношения к командам).

После нажатия кнопки команды начинают отображаться корректно (когда они недоступны, они отключаются и включаются, если доступны).

Редактировать: получается, что не команда нажимает кнопку, чтобы заставить команду работать правильно, а кнопка или другие элементы управления в фокусе (например, если я вкладываю в элемент управления, это также позволяет мои команды).

Вот код для команд:

<Window.InputBindings>
    <KeyBinding Command="{x:Static local:MainWindow.Quit}" Key="Q" Modifiers="Ctrl"/>
    <KeyBinding Command="{x:Static local:MainWindow.Disconnect}" Key="D" Modifiers="Ctrl"/>
</Window.InputBindings>

<Window.ContextMenu>
    <ContextMenu Opacity="95">
        <MenuItem Header="Quit Application                  Ctrl + Q"   Command="{x:Static local:MainWindow.Quit}"/>
        <MenuItem Header="Disconnect from the pump   Ctrl + D" Command="{x:Static local:MainWindow.Disconnect}"/>
    </ContextMenu>
</Window.ContextMenu>

Вот команды CanExecuteMethod:

public static RoutedCommand Quit = new RoutedCommand();   

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

Ответы [ 4 ]

21 голосов
/ 12 сентября 2011

Эта проблема связана с тем, что ContextMenu находится в отдельном визуальном и логическом дереве для окна и его элементов управления.

Для тех, кто все еще ищет ответ на этот вопрос. После обхода Интернета я нашел наиболее эффективный ответ - включить следующее в любое объявление элемента MenuItem, для которого его команды должны быть услышаны его «владельцем».

С точки зрения непрофессионала; если вы хотите, чтобы команды вашего контекстного меню были услышаны нажатием правой кнопки мыши. Добавьте этот код:

CommandTarget="{Binding Path=PlacementTarget,
                        RelativeSource={RelativeSource AncestorType=ContextMenu}
               }"

Пример:

    <ContextMenu>
        <MenuItem Header="Close" Command="Application.Close"
                  CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
    </ContextMenu>

Это также будет работать в шаблонах (я обнаружил, что многие другие решения не поддерживают). Вот объяснение значения заявления, взятого из другого места (я ужасен при объяснении вещей):

Каждый FrameworkElement имеет DataContext, который является произвольным объектом. Источником привязки данных по умолчанию является DataContext. Вы можете использовать RelativeSource.Self, чтобы изменить источник для привязки к самому FrameworkElement вместо его DataContext. Таким образом, часть RelativeSource просто перемещает вас «на один уровень» вверх от DataContext FrameworkElement до самого FrameworkElement. Когда вы находитесь в FrameworkElement, вы можете указать путь к любому из его свойств. Если FrameworkElement является Popup, у него будет свойство PlacementTarget, которое является другим FrameworkElement, к которому Popup относится.

Короче говоря, если у вас есть всплывающее окно, помещенное, например, относительно TextBox, это выражение устанавливает DataContext из всплывающего окна в TextBox, и в результате {Binding Text} где-нибудь в теле всплывающего окна будет привязываться к тексту. текстового поля.

Я искренне надеюсь, что эта информация спасет кого-то, кто плохо знаком с WPF, от головной боли, которую я пережил на этих выходных ... хотя она многому меня научила!

11 голосов
/ 14 октября 2010

Совершенно другой путь, теперь: в ContextMenu действительно есть нечто особенное как носитель для команд: меню не рассматривается как часть окна и, следовательно, не ведет себя так, как элемент в его визуальном дереве.

Существуют различные решения для ваших проблем, определенных здесь: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

Кажется, что проще всего добавить это к вашему XAML (для окна):

FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}"
2 голосов
/ 01 апреля 2016

Я только что натолкнулся на это, пытаясь реализовать пользовательское контекстное меню для AvalonDock.Ни одно из предложенных выше решений не сработало для меня.

Я получил работающее контекстное меню, явно зарегистрировав мои обработчики команд в классе ContextMenu в дополнение к основной вдове.Приведенная ниже функция является помощником, который я использовал для регистрации команд.

    void RegisterCmd(RoutedCommand command, ExecutedRoutedEventHandler handler, CanExecuteRoutedEventHandler canExecute)
    {
        var binding = new CommandBinding(command, handler, canExecute);
        this.CommandBindings.Add(binding);
        CommandManager.RegisterClassCommandBinding(typeof(ContextMenu), binding);
    }
1 голос
/ 13 октября 2010

Возможно, есть некоторые изменения "за кулисами", которые обычно включают команды, но представление не знает об этом изменении.Чтобы увидеть более точные подсказки, нужно было бы увидеть реализации команд.

Вы можете либо сделать все, что изменяет ваше состояние команды enable, уведомить представление или вручную запустить обновление команды через CommandManager.InvalidateRequerySuggested(), дляпример, когда открывается контекстное меню.

WPF ICommands работают таким образом;они запрашивают свою функцию CanExecute всякий раз, когда что-то в представлении изменяется (например, происходит событие PropertyChanged или нажимается кнопка), но они не запрашивают, если у них нет причины.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...