2012-10-17 14 views
19

Tình huống sau. Tôi đã có một UserControl với năm keybindings. Khi TextBox có tiêu điểm, các keybindings của UserControl ngừng kích hoạt ..KeyBinding trong UserControl không hoạt động khi TextBox có tiêu điểm

Có cách nào để khắc phục 'sự cố' này không?

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

Có vẻ như các phím chức năng (F1 vv) và ALT +[key] làm việc. Tôi giả sử CTRLSHIFT công cụ sửa đổi bằng cách nào đó 'chặn' sự kiện này khỏi sủi bọt lên đến UserControl.

+0

hộp văn bản nằm trong bảng điều khiển người dùng? –

+0

@DJ chính xác. Các UserControl là container và (trong ví dụ này) một TextBox là bên trong nó. Nó cũng hoạt động cho ComboBox, DataGrid, vv –

Trả lời

38

Lý do một số kết buộc đầu vào hoạt động và một số không phải là điều khiển TextBox bắt và xử lý một số ràng buộc chính. Ví dụ, nó xử lý CTRL +V cho dán, CTRL +Home cho đi đến đầu văn bản, vv tổ hợp phím khác như CTRL +F3 mặt khác aren 't xử lý bởi TextBox, và vì vậy họ sẽ bong bóng lên.

Nếu bạn chỉ muốn vô hiệu hóa ràng buộc đầu vào của TextBox, điều đó sẽ đơn giản - bạn có thể sử dụng lệnh ApplicationCommands.NotACommand, điều này sẽ vô hiệu hóa hành vi mặc định. Ví dụ, trong trường hợp sau đây, dán với CTRL +V sẽ bị vô hiệu:

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

Tuy nhiên, làm cho nó bong bóng lên đến điều khiển người dùng là một chút phức tạp hơn. Đề xuất của tôi là tạo một hành vi đính kèm sẽ được áp dụng cho UserControl, đăng ký sự kiện PreviewKeyDown và thực thi các ràng buộc đầu vào của nó khi cần thiết trước khi chúng đến được TextBox. Điều này sẽ ưu tiên cho UserControl khi các ràng buộc đầu vào được thực hiện.

Tôi đã viết một hành vi cơ bản mà đạt được chức năng này để giúp bạn bắt đầu:

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

Cách sử dụng:

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

Hope this helps.

+0

Tôi thích nó! Sẽ thực hiện một số thử nghiệm, cảm ơn –

+0

@AdiLester Tôi đã thử giải pháp của bạn. Tôi vừa thay thế biểu thức lambda của bạn bằng 'kb => kb.Key == Key.Enter'. Tôi cũng thay thế 'Key' trong' KeyBinding' từ 'Home' thành' Enter'. Và gỡ bỏ 'Modifiers'. My TextBox nằm bên trong DataGridColumnHeader. Vì vậy, tôi đã áp dụng thuộc tính đính kèm cho DataGrid. Bây giờ, khi tôi nhấn bất kỳ phím nào ngoài Enter, ví dụ: Phím mũi tên xuống, lệnh cháy. Tôi chỉ muốn lệnh của mình kích hoạt khi tôi nhấn phím Enter. Bạn có thể vui lòng cung cấp một số thay đổi đối với mã được đề cập ở trên không ???? – Vishal

+0

@Adi Lester PreviewKeyDown + = KeyEventHandler mới nơi bạn hủy đăng ký khỏi sự kiện? – Gilad

4

Giải pháp của Adi Lester hoạt động tốt. Đây là một giải pháp tương tự khi sử dụng Hành vi. Mã 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(); 
    } 

} 

Và 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 là tên của container của mục tiêu của bạn uielement

nhớ để thiết lập Focusable="True" trong usercontrol

0

Ngoài ra Adi Lester (câu trả lời rất hữu ích) của tôi, tôi muốn đề xuất một số cải tiến/tiện ích giúp tôi triển khai.

Gesture.Matches

Các foundBinding cũng có thể được thực hiện bằng cách gọi Gesture.Matches. Thay đổi các truy vấn LINQ foundBinding như sau:

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

MouseBinding

Bên cạnh đó bạn cũng có thể xác định MouseBindings.

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

Bạn cũng cần phải đăng ký với PreviewMouseEvents ví dụ: PreviewMouseUp và PreviewMouseDoubleClick. Việc thực hiện sau đó gần như giống như đối với 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); 
     } 
    } 
}