WPF:选项卡的键绑定,吞下选项卡并且不传递它

发布于 2024-09-13 21:23:36 字数 239 浏览 8 评论 0原文

我有一个文本框,里面有:

问题是它吞掉了 Tab,并且不会跳到下一个控件。 如何捕获文本框的 Tab 键并仍然保留 Tab 键切换到 Tab 键顺序中的下一个控件? 编辑:我也在使用 MVVM 并且 MyCommand 在 ViewModel 代码中,所以这就是我需要重新抛出选项卡的地方。

I've got a textbox where I have this:
<KeyBinding Command="{Binding MyCommand}" Key="Tab"/>

Problem is it swallows the Tab and doesn't tab to the next control.
How can I trap the Tab for the textbox and still preserve tabbing to the next control in the tab order?
Edit: I'm also using MVVM and MyCommand is in the ViewModel code, so that's where I need to re-throw the Tab.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

枯寂 2024-09-20 21:23:36

这很容易实现,只是不要为此使用 KeyBinding 即可。处理 TextBox 的 OnKeyDown 事件:

<TextBox KeyDown="UIElement_OnKeyDown" ...

然后在代码隐藏中,每当按下 Tab 时执行命令。与 KeyBinding 不同,这不会吞噬 TextInput 事件,因此它应该可以工作。

    private void OnKeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Tab:
                // Execute your command. Something similar to:
                ((YourDataContextType)DataContext).MyCommand.Execute(parameter:null);
                break;
        }
    }

It's easy to achieve, just don't use KeyBinding for this. Handle your TextBox's OnKeyDown event:

<TextBox KeyDown="UIElement_OnKeyDown" ...

Then on the code-behind, execute your command whenever Tab is pressed. Unlike KeyBinding, this won't swallow the TextInput event so it should work.

    private void OnKeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.Tab:
                // Execute your command. Something similar to:
                ((YourDataContextType)DataContext).MyCommand.Execute(parameter:null);
                break;
        }
    }
你的呼吸 2024-09-20 21:23:36

鉴于您的问题作为纯粹的 XAML 解决方案,我找不到将焦点设置为控件的方法。
我选择创建一个 attacted 属性,然后通过绑定将焦点设置为与 ViewModel 中的 KeyBinding 关联的命令中的下一个控件。

这是视图:

<Window x:Class="WarpTab.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:WarpTab.Commands" 
  xmlns:Views="clr-namespace:WarpTab.Views" 
  xmlns:local="clr-namespace:WarpTab.ViewModels" 
  Title="Main Window" Height="400" Width="800">

  <Window.Resources>
      <c:CommandReference x:Key="MyCommandReference" Command="{Binding MyCommand}" />
  </Window.Resources>

  <DockPanel>
    <ScrollViewer>
      <WrapPanel >
        <TextBox Text="First text value" >
            <TextBox.InputBindings>
                <KeyBinding Command="{StaticResource MyCommandReference}" Key="Tab"/>
            </TextBox.InputBindings>
        </TextBox>
        <TextBox Text="Next text value" local:FocusExtension.IsFocused="{Binding FocusControl}"  />
        <Button Content="My Button" />
      </WrapPanel>
    </ScrollViewer>
  </DockPanel>
</Window>

这是 ViewModel:

using System.Windows.Input;
using WarpTab.Commands;

namespace WarpTab.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    public ICommand MyCommand { get; set; }
    public MainViewModel()
    {
      MyCommand = new DelegateCommand<object>(OnMyCommand, CanMyCommand);
    }

    private void OnMyCommand(object obj)
    {
      FocusControl = true;

      // process command here

      // reset to allow tab to continue to work
      FocusControl = false;
      return;
    }

    private bool CanMyCommand(object obj)
    {
      return true;
    }

    private bool _focusControl = false;
    public bool FocusControl
    {
      get
      {
        return _focusControl;
      }
      set
      {
        _focusControl = value;
        OnPropertyChanged("FocusControl");
      }
    }
  }
}

这是定义我在以下内容中找到的附加属性的代码 答案

using System.Windows;

namespace WarpTab.ViewModels
{
  public static class FocusExtension
  {
    public static bool GetIsFocused(DependencyObject obj)
    {
      return (bool)obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value)
    {
      obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.RegisterAttached(
            "IsFocused", typeof(bool), typeof(FocusExtension),
            new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
    {
      var uie = (UIElement)d;
      if ((bool)e.NewValue)
      {
        uie.Focus(); // Don't care about false values. 
      }
    }
  }
}

I cannot find a way to set focus to a control given your question as a purely XAML solution.
I choose to create an attacted property and then through binding set the focus to next control from the Command associated with your KeyBinding in the ViewModel.

Here is the View:

<Window x:Class="WarpTab.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:WarpTab.Commands" 
  xmlns:Views="clr-namespace:WarpTab.Views" 
  xmlns:local="clr-namespace:WarpTab.ViewModels" 
  Title="Main Window" Height="400" Width="800">

  <Window.Resources>
      <c:CommandReference x:Key="MyCommandReference" Command="{Binding MyCommand}" />
  </Window.Resources>

  <DockPanel>
    <ScrollViewer>
      <WrapPanel >
        <TextBox Text="First text value" >
            <TextBox.InputBindings>
                <KeyBinding Command="{StaticResource MyCommandReference}" Key="Tab"/>
            </TextBox.InputBindings>
        </TextBox>
        <TextBox Text="Next text value" local:FocusExtension.IsFocused="{Binding FocusControl}"  />
        <Button Content="My Button" />
      </WrapPanel>
    </ScrollViewer>
  </DockPanel>
</Window>

Here is the ViewModel:

using System.Windows.Input;
using WarpTab.Commands;

namespace WarpTab.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    public ICommand MyCommand { get; set; }
    public MainViewModel()
    {
      MyCommand = new DelegateCommand<object>(OnMyCommand, CanMyCommand);
    }

    private void OnMyCommand(object obj)
    {
      FocusControl = true;

      // process command here

      // reset to allow tab to continue to work
      FocusControl = false;
      return;
    }

    private bool CanMyCommand(object obj)
    {
      return true;
    }

    private bool _focusControl = false;
    public bool FocusControl
    {
      get
      {
        return _focusControl;
      }
      set
      {
        _focusControl = value;
        OnPropertyChanged("FocusControl");
      }
    }
  }
}

Here is the code to define the attached property that I found in the following answer.

using System.Windows;

namespace WarpTab.ViewModels
{
  public static class FocusExtension
  {
    public static bool GetIsFocused(DependencyObject obj)
    {
      return (bool)obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value)
    {
      obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.RegisterAttached(
            "IsFocused", typeof(bool), typeof(FocusExtension),
            new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
    {
      var uie = (UIElement)d;
      if ((bool)e.NewValue)
      {
        uie.Focus(); // Don't care about false values. 
      }
    }
  }
}
与风相奔跑 2024-09-20 21:23:36

为什么不在命令处理程序中使用此代码?

private void MyCommandHandler(){

    // Do command's work here

    TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
    request.Wrapped = true;
    control.MoveFocus(request);

}

这基本上就是“Tab”所做的事情,所以如果您也这样做,就可以开始了。 (当然,如果你有一个带有 Shift-Tab 的命令,请反转方向。

我实际上将其包装到一个扩展方法中,就像这样

public static class NavigationHelpers{

    public static void MoveFocus(this FrameworkElement control, FocusNavigationDirection direction = FocusNavigationDirection.Next, bool wrap = true) {

        TraversalRequest request = new TraversalRequest(direction);
        request.Wrapped = wrap;
        control.MoveFocus(request);

    }

}

......意味着先前的代码变得更加简单,就像这样......

private void MyCommandHandler(){

    // Do command's work here

    Control.MoveFocus();

}

并且如果您不知道当前关注的控件是什么,您可以这样做...

(Keyboard.FocusedElement as FrameworkElement).MoveFocus();

希望这会有所帮助!如果您投票支持我或将其标记为已接受,我们将不胜感激!

Why don't you just use this code in your command handler?

private void MyCommandHandler(){

    // Do command's work here

    TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
    request.Wrapped = true;
    control.MoveFocus(request);

}

That's basically what 'Tab' does, so if you do the same, you're good to go. (Of course reverse the direction if you have a command with Shift-Tab.

I actually wrapped this into an extension method like so...

public static class NavigationHelpers{

    public static void MoveFocus(this FrameworkElement control, FocusNavigationDirection direction = FocusNavigationDirection.Next, bool wrap = true) {

        TraversalRequest request = new TraversalRequest(direction);
        request.Wrapped = wrap;
        control.MoveFocus(request);

    }

}

...meaning the prior code becomes even simpler, like this...

private void MyCommandHandler(){

    // Do command's work here

    Control.MoveFocus();

}

...and if you don't know what the currently focused control is, you can just do this...

(Keyboard.FocusedElement as FrameworkElement).MoveFocus();

Hope this helps! If so, much appreciated if you vote me up or mark it as accepted!

夜巴黎 2024-09-20 21:23:36

遇到了同样的问题,遇到了这个线程,花了我一段时间才找到最好的答案。参考:在特定键上使用 EventTrigger
定义此类:

using System; using System.Windows.Input; using System.Windows.Interactivity;

public class KeyDownEventTrigger : EventTrigger
{

    public KeyDownEventTrigger() : base("KeyDown")
    {
    }

    protected override void OnEvent(EventArgs eventArgs)
    {
        var e = eventArgs as KeyEventArgs;
        if (e != null && e.Key == Key.Tab)
        { 
            this.InvokeActions(eventArgs);                
        }
    }
}

文本框的 xaml:

<TextBox x:Name="txtZip"
     Text="{Binding Zip, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
    <KeyBinding Key="Enter" Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
</TextBox.InputBindings>
<i:Interaction.Triggers>
    <iCustom:KeyDownEventTrigger EventName="KeyDown">
        <i:InvokeCommandAction Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
    </iCustom:KeyDownEventTrigger>
</i:Interaction.Triggers>
</TextBox>

在窗口或用户控件根标记中包含以下属性:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:iCustom="clr-namespace:[NAMESPACE FOR CUSTOM KEY DOWN CLASS]"

Had the same problem, came across this thread and took me a while to find the best answer. Reference: Use EventTrigger on a specific key
Define this class:

using System; using System.Windows.Input; using System.Windows.Interactivity;

public class KeyDownEventTrigger : EventTrigger
{

    public KeyDownEventTrigger() : base("KeyDown")
    {
    }

    protected override void OnEvent(EventArgs eventArgs)
    {
        var e = eventArgs as KeyEventArgs;
        if (e != null && e.Key == Key.Tab)
        { 
            this.InvokeActions(eventArgs);                
        }
    }
}

The xaml for your text box:

<TextBox x:Name="txtZip"
     Text="{Binding Zip, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
    <KeyBinding Key="Enter" Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
</TextBox.InputBindings>
<i:Interaction.Triggers>
    <iCustom:KeyDownEventTrigger EventName="KeyDown">
        <i:InvokeCommandAction Command="{Binding ZipLookup.GetAddressByZipKeyCommand}" CommandParameter="{Binding ElementName=txtZip, Path=Text}" />
    </iCustom:KeyDownEventTrigger>
</i:Interaction.Triggers>
</TextBox>

In your window or user control root tag include these attributes:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:iCustom="clr-namespace:[NAMESPACE FOR CUSTOM KEY DOWN CLASS]"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文