web-dev-qa-db-fra.com

La KeyBinding dans UserControl ne fonctionne pas quand TextBox est le focus

La situation suivante J'ai un UserControl avec cinq raccourcis. Lorsque la zone de texte a le focus, les combinaisons de touches de UserControl cessent de se déclencher.

Y a-t-il un moyen de résoudre ce "problème"?

<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>

Il semble que les touches de fonction (F1 etc) et ALT+[key] faire du travail. Je présume le CTRL et SHIFT Les modificateurs empêchent en quelque sorte que l'événement ne se propage à UserControl.

20
Ralf de Kleine

Certaines liaisons d'entrée fonctionnent, mais d'autres non, parce que le contrôle TextBox intercepte et gère certaines liaisons de clés. Par exemple, il gère CTRL+V pour la pâte, CTRL+Home pour aller au début du texte, etc. D'autres combinaisons de touches telles que CTRL+F3 Par contre, ils ne sont pas gérés par la TextBox, ils vont donc bouillonner.

Si vous vouliez simplement désactiver la liaison d'entrée de la zone de texte, ce serait simple: vous pouvez utiliser la commande ApplicationCommands.NotACommand, qui désactiverait le comportement par défaut. Par exemple, dans le cas suivant, coller avec CTRL+V sera désactivé:

<TextBox>
    <TextBox.InputBindings>
        <KeyBinding Key="V" Modifiers="Control" Command="ApplicationCommands.NotACommand" />
    </TextBox.InputBindings>
</TextBox>

Cependant, le faire remonter au contrôle de l'utilisateur est un peu plus délicat. Ma suggestion est de créer un comportement attaché qui sera appliqué à UserControl, inscrivez-vous à son événement PreviewKeyDown et exécutez ses liaisons d'entrée si nécessaire avant qu'elles n'atteignent la zone de texte. Cela donnera la priorité à UserControl lorsque les liaisons d’entrée sont exécutées.

J'ai écrit un comportement de base qui réalise cette fonctionnalité pour vous aider à démarrer:

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

Usage:

<UserControl local:InputBindingsBehavior.TakesInputBindingPrecedence="True">
    <UserControl.InputBindings>
        <KeyBinding Key="Home" Modifiers="Control" Command="{Binding MyCommand}" />
    </UserControl.InputBindings>
    <TextBox ... />
</UserControl>

J'espère que cela t'aides.

44
Adi Lester

La solution d'Adi Lester fonctionne bien. Voici une solution similaire utilisant Behavior. Le code C #:

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();
    }

}

Et le 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>
4
JF Moreau

En plus de sa réponse (très utile) à Adi Lester, je voudrais suggérer quelques améliorations/extensions qui m'ont aidé dans ma mise en œuvre.

Gesture.Matches

Le foundBinding peut également être effectué en appelant Gesture.Matches. Modifiez la requête foundBinding Linq comme suit:

KeyBinding foundBinding = ((UIElement)this).InputBindings
            .OfType<KeyBinding>()
            .FirstOrDefault(inputBinding => inputBinding.Gesture.Matches(sender, eventArgs));

MouseBinding

En outre, vous pouvez également définir MouseBindings.

<MouseBinding Command="{Binding DataContext.AddInputValueCommand, ElementName=root}" CommandParameter="{Binding}" Gesture="Shift+MiddleClick" />

Vous devez également vous abonner à PreviewMouseEvents, par exemple. PreviewMouseUp et PreviewMouseDoubleClick. L'implémentation est alors presque la même que pour 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);
        }
    }
}
1
J. Voermans
<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 est le nom du conteneur de votre cible uielement

n'oubliez pas de définir le Focusable="True" dans usercontrol

0
mao