2012-10-17 12 views
19

Następująca sytuacja. Mam UserControl z pięcioma skrótami klawiszowymi. Gdy TextBox ma fokus, klawisze sterowania UserControl przestają strzelać.KeyBinding w UserControl nie działa, gdy TextBox ma fokus.

Czy istnieje sposób naprawienia tego "problemu"?

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

Wydaje klawisze funkcyjne (F1etc) i ALT + [key] zrobić pracę. Domyślam się, że modyfikatory są w jakiś sposób "blokujące" wydarzenie od bulgotania do kontrolki UserControl.

+0

textbox znajduje się wewnątrz kontrolki użytkownika? –

+0

@ DJ dokładnie. UserControl jest kontenerem i (w tym przykładzie) znajduje się w nim TextBox. Działa również dla ComboBox, DataGrid itp. –

Odpowiedz

38

Powód, dla którego niektóre powiązania wejściowe działają, a niektóre nie, polega na tym, że kontrolka TextBox przechwytuje i obsługuje niektóre powiązania klawiszy. Na przykład, obsługuje CTRL + V do pasty, CTRL + Home aby jechać do początku tekstu, itp Inne kombinacje klawiszy takich jak CTRL + F3 drugiej strony aren 't obsługiwane przez TextBox, a więc będą się bańką.

Jeśli chcesz wyłączyć powiązanie wejściowe TextBox, byłoby to proste - można użyć polecenia ApplicationCommands.NotACommand, które wyłączy domyślne zachowanie. Na przykład w poniższym przypadku wklejania z CTRL + V zostaną wyłączone:

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

Jednak, dzięki czemu bańki do kontroli użytkownika jest nieco trudniejsze. Moją sugestią jest utworzenie powiązanego zachowania, które zostanie zastosowane do UserControl, zarejestrowanie się w jego zdarzeniu PreviewKeyDown i wykonanie jego powiązań wejściowych w razie potrzeby, zanim dotrą one do TextBox. Da to pierwszeństwo kontroli UserControl po wykonaniu powiązań wejściowych.

Napisałem podstawowy problem, który osiąga tę funkcję, aby zacząć:

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

Zastosowanie:

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

Nadzieja to pomaga.

+0

Podoba mi się! Zrobię kilka eksperymentów, dzięki –

+0

@ AdiLester Próbowałem Twojego rozwiązania. Właśnie zamieniłem twoje wyrażenie lambda na 'kb => kb.Key == Key.Enter'. Zastąpiłem również 'Key' w' KeyBinding' z 'Home' na' Enter'. I usunięto "Modyfikatory". Mój TextBox znajduje się wewnątrz DataGridColumnHeader. Więc zastosowałem dołączoną właściwość do DataGrid. Teraz, kiedy naciskam dowolny klawisz inny niż Enter, na przykład Klawisz strzałki w dół powoduje uruchomienie polecenia. Chcę tylko, żeby moje polecenie się odpaliło po naciśnięciu klawisza Enter. Czy możesz podać kilka zmian w powyższym kodzie? – Vishal

+0

@Adi Lester PreviewKeyDown + = new KeyEventHandler skąd nie bierzesz udziału w wydarzeniu? – Gilad

4

Rozwiązanie Adi Lester działa dobrze. Oto podobne rozwiązanie za pomocą zachowania. Kod 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(); 
    } 

} 

A 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> 
0
<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 to nazwa pojemnika docelowego uielement

pamiętać, aby ustawić Focusable="True" w usercontrol

0

Ponadto do Adi Lester jego (bardzo pomocna) odpowiedź Chciałbym zaproponować kilka ulepszeń/rozszerzeń, które pomogły mi w mojej implementacji.

Gesture.Matches

The foundBinding można również dokonać poprzez wywołanie Gesture.Matches. Zmienić zapytanie foundBinding LINQ do następujących:

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

MouseBinding

Ponadto można również definiować MouseBindings.

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

Następnie należy również subskrybować PreviewMouseEvents np. PreviewMouseUp i PreviewMouseDoubleClick. Implementacja jest prawie taka sama jak w przypadku 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); 
     } 
    } 
} 
Powiązane problemy