У меня есть надстройка VSTO (Excel или Word) с диалоговым окном WPF, показанным модально. Диалог имеет TextBox, на котором я должен быть изначально сфокусирован. Я могу сфокусировать его, используя различные методы, такие как классы FocusManager
или Keyboard
, или запросив Traversal
. Или я могу смоделировать нажатие клавиши TAB через keybd_event()
(user32.dll).
Проблема с любым из этих методов, поле не кажется "полностью" сфокусированным. Курсор отображается в TextBox, но он не мигает и набор текста не работает! Чтобы решить проблему, я могу либо:
нажмите клавишу TAB один раз (не программно), и курсор начнет мигать, и я смогу печатать; или
Нажмите Alt-Tab, чтобы переключиться на другое приложение, затем вернитесь. Снова курсор начнет мигать, и я могу напечатать.
РЕДАКТИРОВАТЬ: РЕШЕНИЕ
Базовый подход ErrCode работает надежно, с некоторыми изменениями. Во-первых, вместо того, чтобы делать его идею только один раз, я делаю это на всех окнах. Во-вторых, я открываю фиктивное окно после загрузки собственного окна, а не раньше. Наконец, я ввожу некоторые задержки, без которых подход не работает:
// 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
}
Чтобы применить, в пользовательском элементе управления или окне XAML добавьте:
FocusExtensions.FocusElementOnLoaded="{Binding ElementName=MyTextBox}"
MyTextBox, конечно, должен быть производным от IInputElement
.