开发者

WPF focus management in .Net 4.0

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 th开发者_开发知识库e 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.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜