.Net 4.0 中的 WPF 焦点管理

发布于 2024-12-01 03:47:18 字数 1935 浏览 0 评论 0原文

我在 WPF 中有一个仅由键盘使用的应用程序,因此我们对焦点行为非常挑剔。

因此,我们在文本框上获得了 PreviewLostKeyboardFocus。在某些情况下,我们会禁用接下来的 5 个字段,并希望焦点转到之后的字段。人们可能会认为焦点会这样做,找到下一个可聚焦字段,如果我没有在预览事件中禁用这些字段,就会发生这种情况。事实并非如此,它将焦点保持在第一个文本框上。

我尝试使用 Keyboard.Focus(uielement) 强制聚焦,但没有任何反应。看来下一个重点目标已经确定了。

我怎样才能做到这一点,或者我“做错了”?我无法更改指定此行为的要求;我知道这有点奇怪。

谢谢。

编辑:这是一个显示此行为的小应用程序。 XAML:

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    FocusManager.FocusedElement="{Binding ElementName=textBox0}"
    >
  <StackPanel>
    <TextBox Height="23" Margin="5" Name="textBox0" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox1" Width="120" PreviewLostKeyboardFocus="textBox1_PreviewLostKeyboardFocus"/>
    <TextBox Height="23" Margin="5" Name="textBox2" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox3" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox4" Width="120" />
  </StackPanel>
</Window>

代码隐藏:

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

namespace WpfApplication4 {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
        private void textBox1_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
            textBox2.IsEnabled = false;
            textBox3.IsEnabled = false;
        }
    }
}

一个明显的尝试(至少对我来说)是将 Keyboard.focus(textBox4); 放入 PreviewLostKeyboardFocus 事件处理程序中。它当然不起作用,它会导致一个循环再次触发 PreviewLostKeyboardFocus 事件......

又一个编辑: 我发现在 textBox1_PreviewLostKeyboardFocus() 中使用断点有时会导致其正常运行,有时甚至不会禁用第二个和第三个文本框。我正在考虑竞争/线程问题。

I have an application in WPF that is to be used exclusively by the keyboard, so we are really picky about focus behavior.

So, we get a PreviewLostKeyboardFocus on a textbox. Under certain circumstances we disable the next 5 fields and want the focus to go to the field after that. One might assume that the focus would do that, finding the next focusable field, this is what happens if I didn't disable the fields in the preview event. It doesn't, it keeps the focus on the first textbox.

I've tried forcing the focus with Keyboard.Focus(uielement) but nothing happens. It seems that the next focus target is already commited.

How can I make this happen, or am I "doing it wrong"? I am not in a position to change the requirement that specifies this behavior; I know that it is somewhat strange.

Thanks.

Edit: here is a small app that shows this behavior.
XAML:

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    FocusManager.FocusedElement="{Binding ElementName=textBox0}"
    >
  <StackPanel>
    <TextBox Height="23" Margin="5" Name="textBox0" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox1" Width="120" PreviewLostKeyboardFocus="textBox1_PreviewLostKeyboardFocus"/>
    <TextBox Height="23" Margin="5" Name="textBox2" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox3" Width="120" />
    <TextBox Height="23" Margin="5" Name="textBox4" Width="120" />
  </StackPanel>
</Window>

Codebehind:

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

namespace WpfApplication4 {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
        private void textBox1_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
            textBox2.IsEnabled = false;
            textBox3.IsEnabled = false;
        }
    }
}

An obvious thing to try (at least for me) was to put Keyboard.focus(textBox4); in the PreviewLostKeyboardFocus event handler. It of course didn't work, it causes a loop that fires the PreviewLostKeyboardFocus event again....

Yet another edit:
I've found that using breakpoints in textBox1_PreviewLostKeyboardFocus() will sometimes cause it to behave, or sometimes not even disable the 2nd and 3rd text boxes. I'm thinking of a race/threading problem.

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

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

发布评论

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

评论(2

白鸥掠海 2024-12-08 03:47:18

我不确定到底是什么导致了这种行为,但从过去的经验来看,WPF 焦点系统在以任何方式更改控件后或手动设置焦点时都非常不可靠。

但是,在 WPF 赶上控件的更改后使用调度程序执行焦点更改通常可以解决问题。

这在我的测试中效果很好

private void textBox1_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
    textBox2.IsEnabled = false;
    textBox3.IsEnabled = false;

    this.Dispatcher
        .BeginInvoke(new Action(() => Keyboard.Focus(textBox4)), 
        System.Windows.Threading.DispatcherPriority.Input, null);
}

I am not sure what exactly is causing this behavior but from past experience the WPF focus system is extremely unreliable after changing the controls in any way or when setting focus manually.

However, using a Dispatcher to perform focus changes after WPF caught up with the changes to the controls, often solves the problem.

This works fine in my testing

private void textBox1_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
    textBox2.IsEnabled = false;
    textBox3.IsEnabled = false;

    this.Dispatcher
        .BeginInvoke(new Action(() => Keyboard.Focus(textBox4)), 
        System.Windows.Threading.DispatcherPriority.Input, null);
}
污味仙女 2024-12-08 03:47:18

您也许可以通过视图模型更好地做到这一点。您的视图模型可以设置布尔属性来启用/禁用字段。我确信您不想淹没这样的视图模型,但这可能会使应用程序行为更加可预测。我认为 wpf 知道跳过禁用控件上的制表符停止位,并且您也许能够在选项卡中找到所需的选项卡行为导航。

You might be able to do this better via the viewmodel. Your view model could set bool properties to enable/disable fields. I"m sure you'd rather not flood a viewmodel like that, but it may make the app behavior more predicable. I think wpf knows to skip tabstops on disabled controls, and you may be able to find a desired tab behavior in the tab navigation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文