I have a VSTO (Excel or Word) addin with a WPF dialog window, shown modally. The dialog has a TextBox that I need to be initially focused. I can get it to focus using various methods like the FocusManager
or Keyboard
classes, or by requesting a Traversal
. Or I can simulate a TAB key press via keybd_event()
(user32.dll).
The problem is with any of these methods, the field doesn't seem to be "fully" focused. The cursor shows in the TextBox, but it isn't blinking and typing won't work! To solve the problem, I can either:
press TAB once (not programatically) and the cursor will start blinking and I can type; or
Press Alt-Tab to switch to another application, then back. Again, the cursor will start blinking and I can type.
EDIT: SOLUTION
ErrCode's basic approach works reliably, with some modifications. First, instead of doing his idea just once, I do it on all windows. Second, I open the dummy window after my own window loads, not before. Finally, I introduce some delays, without which the approach doesn't work:
// Window or UserControl, works for both or any FrameworkElement technically.
public static class FocusExtensions
{
#region FocusElementOnLoaded Attached Behavior
public static IInputElement GetFocusElementOnLoaded(FrameworkElement obj)
{
return (IInputElement)obj.GetValue(FocusElementOnLoadedProperty);
}
public static void SetFocusElementOnLoaded(FrameworkElement obj, IInputElement value)
{
obj.SetValue(FocusElementOnLoadedProperty, value);
}
public static readonly DependencyProperty FocusElementOnLoadedProperty =
DependencyProperty.RegisterAttached("FocusElementOnLoaded", typeof(IInputElement), typeof(FocusExtensions), new PropertyMetadata(null, FocusElementOnLoadedChangedCallback));
private static async void FocusElementOnLoadedChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// This cast always succeeds.
var c = (FrameworkElement) d;
var element = (IInputElement) e.NewValue;
if (c != null && element != null)
{
if (c.IsLoaded)
await FocusFirst(element);
else
c.Loaded += async (sender, args) =>
await FocusFirst(element);
}
}
private static async Task FocusFirst(IInputElement firstInputElement)
{
var tmpWindow = new Window
{
Width = 0,
Height = 0,
Visibility = Visibility.Collapsed
};
tmpWindow.Loaded += async (s, e) =>
{
await Task.Delay(1);
Application.Current?.Dispatcher?.Invoke(() =>
{
tmpWindow.Close();
firstInputElement.Focus();
});
};
await Task.Delay(1);
Application.Current?.Dispatcher?.Invoke(() =>
{
tmpWindow.ShowDialog();
});
}
#endregion FocusElementOnLoaded Attached Behavior
}
To apply, in your XAML's UserControl or Window, add:
FocusExtensions.FocusElementOnLoaded="{Binding ElementName=MyTextBox}"
The MyTextBox must of course be derived from IInputElement
.