Пользовательская команда WPF в контекстном меню отключена до тех пор, пока не будет нажата любая кнопка
У меня есть пользовательская команда, и я пытаюсь выполнить их из контекстного меню, но они всегда отображаются как отключенные, если я не нажму кнопку на пользовательском интерфейсе (кнопки не имеют ничего общего с командами).
После нажатия кнопки команды начинают отображаться правильно (когда они недоступны, они становятся недоступными и включаются, если они доступны).
Редактирование: оказывается, что это не кнопка, которая заставляет команду работать правильно, но кнопка или другие элементы управления в фокусе (например, если я вставляю элемент управления, это также позволяет мои команды).
Вот код для команд:
<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;
}
Ответы
Ответ 1
Полностью другой трек, теперь:
есть действительно что-то особенное в ContextMenu в качестве носителя для команд:
меню не рассматривается как часть окна и поэтому не ведет себя как элемент в его визуальном дереве.
Существуют различные решения для ваших проблем, определенных здесь:
http://www.wpftutorial.net/RoutedCommandsInContextMenu.html
Самый простой подход, похоже, добавляет его в ваш XAML (для окна):
FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}"
Ответ 2
Эта проблема связана с тем, что 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 является всплывающим, он будет иметь свойство PlacementTarget, которое является другим элементом FrameworkElement, который всплывает по отношению к.
Короче говоря, если у вас есть Popup, размещенный относительно TextBox, например, это выражение устанавливает DataContext всплывающего окна в TextBox, и в результате {Binding Text} где-то в теле Popup будет привязываться к тексту текстового поля.
Я искренне надеюсь, что эта информация спасет тех, кто новичок в WPF, головную боль, которую я пережил в этот уик-энд... хотя это меня многому научило!
Ответ 3
Вероятно, есть некоторые изменения "за кулисами", которые обычно включали команды, но представление не знает об этом изменении.
Нужно было бы увидеть Command-реализаций, чтобы дать более точные подсказки.
Вы можете либо сделать что-либо, что изменит ваше состояние включения команды, либо уведомить представление, либо вручную запустить обновление команды через CommandManager.InvalidateRequerySuggested()
, например, когда откроется контекстное меню.
ICF файлы WPF работают именно так; они запрашивают свою функцию CanExecute
всякий раз, когда что-то в представлении изменяется (например, событие PropertyChanged-событие запущено или нажимается кнопка), но они не требуют, чтобы у них не было причин.
Ответ 4
Я просто столкнулся с этим, пытаясь реализовать пользовательское контекстное меню для 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);
}