KeyBinding в UserControl не работает, когда TextBox имеет фокус
Следующая ситуация. У меня есть UserControl с пятью ключами. Когда TextBox имеет фокус, привязки клавиш для остановки запуска UserControl.
Есть ли способ исправить эту "проблему"?
<UserControl.InputBindings>
<KeyBinding Key="PageDown" Modifiers="Control" Command="{Binding NextCommand}"></KeyBinding>
<KeyBinding Key="PageUp" Modifiers="Control" Command="{Binding PreviousCommand}"></KeyBinding>
<KeyBinding Key="End" Modifiers="Control" Command="{Binding LastCommand}"></KeyBinding>
<KeyBinding Key="Home" Modifiers="Control" Command="{Binding FirstCommand}"></KeyBinding>
<KeyBinding Key="F" Modifiers="Control" Command="{Binding SetFocusCommand}"></KeyBinding>
</UserControl.InputBindings>
<TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Gesture="Enter" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl }}, Path=DataContext.FilterCommand}"></KeyBinding>
</TextBox.InputBindings>
</TextBox>
Кажется, что функциональные клавиши (F1 и т.д.) и ALT + [key] работают. Я предполагаю, что модификаторы CTRL и SHIFT как-то блокируют событие от пузырьков до UserControl.
Ответы
Ответ 1
Причина того, что некоторые привязки ввода работают, а некоторые нет, это то, что элемент управления TextBox ловит и обрабатывает некоторые привязки клавиш. Например, он обрабатывает CTRL + V для вставки, CTRL + Home для перехода к началу текста и т.д. Другие комбинации клавиш, такие как CTRL + F3, с другой стороны, aren 'обрабатывается TextBox, и поэтому они будут пузыриться.
Если вы просто хотели отключить привязку ввода TextBox, это было бы просто - вы могли бы использовать команду ApplicationCommands.NotACommand
, которая отключила бы поведение по умолчанию. Например, в следующем случае вставка с CTRL + V будет отключена:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="V" Modifiers="Control" Command="ApplicationCommands.NotACommand" />
</TextBox.InputBindings>
</TextBox>
Тем не менее, заставляя его зависнуть до пользовательского элемента управления немного сложнее. Мое предложение состоит в том, чтобы создать прикрепленное поведение, которое будет применяться к UserControl, зарегистрировать его событие PreviewKeyDown
и выполнить необходимые привязки для ввода до того, как они достигнут TextBox. Это будет иметь приоритет для UserControl при выполнении привязок ввода.
Я написал базовое поведение, которое обеспечивает эту функциональность, чтобы вы начали:
public class InputBindingsBehavior
{
public static readonly DependencyProperty TakesInputBindingPrecedenceProperty =
DependencyProperty.RegisterAttached("TakesInputBindingPrecedence", typeof(bool), typeof(InputBindingsBehavior), new UIPropertyMetadata(false, OnTakesInputBindingPrecedenceChanged));
public static bool GetTakesInputBindingPrecedence(UIElement obj)
{
return (bool)obj.GetValue(TakesInputBindingPrecedenceProperty);
}
public static void SetTakesInputBindingPrecedence(UIElement obj, bool value)
{
obj.SetValue(TakesInputBindingPrecedenceProperty, value);
}
private static void OnTakesInputBindingPrecedenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UIElement)d).PreviewKeyDown += new KeyEventHandler(InputBindingsBehavior_PreviewKeyDown);
}
private static void InputBindingsBehavior_PreviewKeyDown(object sender, KeyEventArgs e)
{
var uielement = (UIElement)sender;
var foundBinding = uielement.InputBindings
.OfType<KeyBinding>()
.FirstOrDefault(kb => kb.Key == e.Key && kb.Modifiers == e.KeyboardDevice.Modifiers);
if (foundBinding != null)
{
e.Handled = true;
if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
{
foundBinding.Command.Execute(foundBinding.CommandParameter);
}
}
}
}
Использование:
<UserControl local:InputBindingsBehavior.TakesInputBindingPrecedence="True">
<UserControl.InputBindings>
<KeyBinding Key="Home" Modifiers="Control" Command="{Binding MyCommand}" />
</UserControl.InputBindings>
<TextBox ... />
</UserControl>
Надеюсь, что это поможет.
Ответ 2
Решение Adi Lester работает хорошо. Вот аналогичное решение, использующее Поведение.
Код С#:
public class AcceptKeyBinding : Behavior<UIElement>
{
private TextBox _textBox;
/// <summary>
/// Subscribes to the PreviewKeyDown event of the <see cref="TextBox"/>.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
_textBox = AssociatedObject as TextBox;
if (_textBox == null)
{
return;
}
_textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;
}
private void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs keyEventArgs)
{
var uielement = (UIElement)sender;
var foundBinding = uielement.InputBindings
.OfType<KeyBinding>()
.FirstOrDefault(kb => kb.Key == keyEventArgs.Key && kb.Modifiers == keyEventArgs.KeyboardDevice.Modifiers);
if (foundBinding != null)
{
keyEventArgs.Handled = true;
if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
{
foundBinding.Command.Execute(foundBinding.CommandParameter);
}
}
}
/// <summary>
/// Unsubscribes to the PreviewKeyDown event of the <see cref="TextBox"/>.
/// </summary>
protected override void OnDetaching()
{
if (_textBox == null)
{
return;
}
_textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;
base.OnDetaching();
}
}
И XAML:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Modifiers="Shift" Command="{Binding CommandManager[ExecuteCommand]}"
CommandParameter="{Binding ExecuteText}" />
</TextBox.InputBindings>
<i:Interaction.Behaviors>
<behaviours:AcceptKeyBinding />
</i:Interaction.Behaviors>
</TextBox>
Ответ 3
В дополнение к Adi Lester его (очень полезный) ответ, я хотел бы предложить некоторые улучшения/расширения, которые помогли мне с моей реализацией.
Gesture.Matches
Обнаружение привязки также можно выполнить, вызвав Gesture.Matches. Измените запрос foundBinding Linq на следующее:
KeyBinding foundBinding = ((UIElement)this).InputBindings
.OfType<KeyBinding>()
.FirstOrDefault(inputBinding => inputBinding.Gesture.Matches(sender, eventArgs));
MouseBinding
Кроме того, вы также можете определить MouseBindings.
<MouseBinding Command="{Binding DataContext.AddInputValueCommand, ElementName=root}" CommandParameter="{Binding}" Gesture="Shift+MiddleClick" />
Затем вам также необходимо подписаться на PreviewMouseEvents, например. PreviewMouseUp и PreviewMouseDoubleClick. Реализация тогда почти такая же, как для KeyBindings.
private void OnTextBoxPreviewMouseUp(object sender, MouseButtonEventArgs eventArgs)
{
MouseBinding foundBinding = ((UIElement)this).InputBindings
.OfType<MouseBinding>()
.FirstOrDefault(inputBinding => inputBinding.Gesture.Matches(sender, eventArgs));
if (foundBinding != null)
{
eventArgs.Handled = true;
if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
{
foundBinding.Command.Execute(foundBinding.CommandParameter);
}
}
}
Ответ 4
<UserControl.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="FocusManager.FocusedElement" Value=" {Binding ElementName=keyPressPlaceHoler}" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
keyPressPlaceHoler
- это имя контейнера вашей цели uielement
не забудьте установить Focusable="True"
в usercontrol