首页 > 解决方案 > VSTO WPF 模式对话框光标在 TextBox 中不闪烁

问题描述

我有一个带有 WPF 对话框窗口的 VSTO(Excel 或 Word)插件,以模态方式显示。该对话框有一个文本框,我需要首先关注它。我可以使用诸如FocusManagerorKeyboard类之类的各种方法或通过请求Traversal. keybd_event()或者我可以通过(user32.dll)模拟 TAB 按键。

问题在于这些方法中的任何一种,该领域似乎都没有“完全”集中。光标显示在文本框中,但它不闪烁,打字也不起作用!为了解决这个问题,我可以:

  1. 按 T​​AB 一次(不是以编程方式),光标将开始闪烁,我可以输入;或者

  2. 按 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 的 UserControl 或 Window 中,添加:

FocusExtensions.FocusElementOnLoaded="{Binding ElementName=MyTextBox}"

MyTextBox 当然必须派生自IInputElement.

标签: c#.netwpfvsto

解决方案


请注意,您提到的所有焦点方法似乎都不是Control.Focus(). 例如:

// Constructor of your window
public MyWindow()
{
    // ...
    // set any DataContext before InitializeComponent() if needed
    // ...
    InitializeComponent();
    // ...
    // add anything else you need to do within the constructor of the window
    // ...
    textbox1.Focus();   // Set focus on your text box
}

这在 Excel VSTO 加载项项目中进行了测试,一旦显示对话框,它就会立即允许键入转到焦点文本框。与您的窗口所有者黑客一起使用(必须在我的项目中做类似的事情)。

Control.Focus() 和 FocusManager.SetFocusedElement() 之间的区别

编辑

这是我从头开始启动 VSTO 加载项项目后发现的内容。它似乎是在 Excel VSTO 加载项中使用 WPF 的众多Control.Focus()怪癖之一,如果将它与有史以来创建的第一个 WPF 窗口一起使用,您将无法可靠地开始工作。

解决方法:创建一个虚拟 WPF 窗口。关闭它。然后创建真正的将使用Control.Focus().

以前从未注意到这个问题,因为我的项目总是在呈现真实窗口之前打开和关闭一个简短的“加载”窗口。

bool isFirstTime = true;
private void button1_Click(object sender, RibbonControlEventArgs e)
{
    if (isFirstTime)
    {
        // For some reason the Control.Focus() is unreliable for the first WPF window ever shown within the VSTO addin. :( 
        // So make a dummy one once before we open the real window...
        // NOTE: The reason why I never noticed such a problem before in my own project, is since my project always showed a short loading window that closed itself
        // before the main window is shown. So I could use Control.Focus() in the main window without issues
        var pretendLoadWindow = new Window();
        pretendLoadWindow.SizeToContent = SizeToContent.WidthAndHeight;
        pretendLoadWindow.Visibility = Visibility.Collapsed;
        pretendLoadWindow.Loaded += (_sender, _e) => pretendLoadWindow.Close();
        pretendLoadWindow.ShowDialog();
        isFirstTime = false;
    }
    var window = new Window();
    var excelHwnd = m_ExcelApplication != null ? new IntPtr(m_ExcelApplication.Hwnd) : Process.GetCurrentProcess().MainWindowHandle;
    WindowInteropHelper interopHelper = new WindowInteropHelper(window)
    {
        Owner = excelHwnd
    };
    window.Content = new UserControl1();
    window.SizeToContent = SizeToContent.WidthAndHeight;
    // FYI: just in case you have any breakpoints that switch the focus away from the Excel (to your VS IDE), then when this window is shown it won't typically get focus. Below should fix this...
    window.Loaded += (_sender, _e) => window.Focus();       
    window.ShowDialog();
}

可从此处访问完整的测试代码


推荐阅读