0
votes

So when my window opens, in the Loaded event, I set

MyTextBox.Focus();

and this works fine, Focus Visual is there, the caret blinks, I can type, no problem. Click on a node in a TreeView which I want to modify the textbox's text and re-focus the TextBox so the user can keep typing. I call:

    MyTextBox.Focus();

one more time. The focus visual does NOT appear on the textbox, and the cursor is NOT in the textbox. However, Keyboard.FocusedElement == MyTextBox is TRUE. Two oddities:

A) I have a button [_Save] on the window. If I just press 'S' (no Alt- modifier), that button is pressed, like the window was taking my keystrokes!

B) if I press TAB on my keyboard, the first control on the window is selected, eventually, I'm able to select the control I want and it works perfectly.

There are no explicit focus scopes set anywhere.

Additionally, if I try traversals or InputManager.Current.ProcessInput(some tabs) or a few other ways to try to course the window into giving MyTextBox the focus properly, none of them work.

This is a regular WPF app without any special styling/templating, and I'm at my wit's end!

2
Did you try Keyboard.Focus(MyTextBox)?mm8
Just an idea - maybe TreeView tries to capture focus after click - try postponing textBox.Focus(); using Dispatcher: textBox.Dispatcher.BeginInvoke((Action)(() => textBox.Focus()));Quercus
@Quercus Sorry, no dice. The Focus() does return true and checking the focused element confirms my textbox, NOT the treeview has the focus. It just doesn't look or act that way. The TreeView isn't stealing the focus. My posted solution does the job, but thanks for the suggestion.Chris Bordeman

2 Answers

0
votes

Set focus back to TextBox in TreeView GotFocus event. Example:

XAML

        <TreeView x:Name="tw" GotFocus="tw_GotFocus">
            <TreeViewItem Header="twh1">
                <TreeViewItem Header="twh2" Selected="TreeViewItem_Selected"></TreeViewItem>
            </TreeViewItem>
        </TreeView>
        <TextBox x:Name="MyTextBox"/>

CS

    private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
    {
        MyTextBox.AppendText(((TreeViewItem)sender).Header.ToString());
        MyTextBox.CaretIndex = MyTextBox.Text.Length;
    }

    private void tw_GotFocus(object sender, RoutedEventArgs e)
    {
        MyTextBox.Focus();
    }
0
votes

OK I've had this issue with an old VSTO based WPF app in the past, and I've adapted a solution I came up with to work here, too. Here's an extension method:

using System.Threading.Tasks;
using System.Windows;

namespace My.Extensions
{
    public static class UiElementExtensions
    {
        /// <summary>
        /// Use when the normal Focus() just refuses to cooperate.
        /// Don't use rapidly in succession unless you rewrite the whole thing to return a Task.
        /// </summary>
        /// <param name="it">The UIElement to give focus.</param>
        /// <param name="delay">Try less than 20 ms at your end users' peril.</param>
        public static void FocusHarder(this UIElement it, int delay = 20)
        {
            var w = new Window { Width = 0, Height = 0, Visibility = Visibility.Hidden };
            w.Loaded += async (_, _) =>
            {
                await Task.Delay(delay);
                it.Focus();
                w.Close();
            };
            w.Show();
        }
    }
}

Use it instead of .Focus(), such as:

MyTextBox.FocusHarder();

I know it's a hack, but I'll be darned if it doesn't work extremely reliably. I'm not sure how long the 20ms delay actually needs to be, but in my testing less than that sometimes doesn't work on some end users' systems.

I'm beginning to think this is a nearly ubiquitous WPF timing bug.