c# - VSTO WPF 模式对话框光标在文本框中不闪烁

标签 c# .net wpf vsto

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

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

  1. 按 TAB 一次(不是以编程方式),光标将开始闪烁,我可以打字;或

  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

最佳答案

注意到您提到的焦点方法似乎都不是 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 加载项项目中进行了测试,一旦显示对话框,它就可以立即输入内容以转到焦点文本框。与您的窗口所有者 hack 一起使用(必须在我的项目中执行类似操作)。

Difference between Control.Focus() and 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();
}

完整的测试代码可从here访问

关于c# - VSTO WPF 模式对话框光标在文本框中不闪烁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56820763/

相关文章:

C# - 如何唤醒休眠线程?

c# - Writer 已关闭或处于错误状态

c# - ASP.NET Core Web Api 发送 Access-Control-Allow-Origin : null CORS header and chrome is erroring, 如何修复?

c++ - Vista 窗口标题中的图片(标识)

c# - 如何在“添加新项”对话框中找到 WPF 自定义控件模板?

c# - 如何连接 xaml 和 xaml.cs 文件

c# - ASP.NET MVC : how to split a row in a text file by fixed-width and pass to a model

c# - 泛型函数(属性)

.net - 使用 .NET 中的 Cyber​​Source API 取消/退款交易

.net - 为什么我的 .NET Web 服务 XML 与其他人的不同?