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



我有一个带有wpf对话框窗口的vsto(excel或word(addin。对话框的文本框我最初需要集中精力。我可以使用FocusManagerKeyboard类(或请求Traversal(等各种方法将其焦点。或者我可以通过 keybd_event()(user32.dll(模拟选项卡键。

问题在于这些方法中的任何一种,该领域似乎并未"完全"集中。光标在文本框中显示,但它并没有闪烁,打字行不通!要解决问题,我可以:

  1. 按下一次(不是在编程上(,光标将开始闪烁,我可以键入;或

  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或窗口中应用:

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一起使用(在我的项目中必须进行类似的操作(。

control.focus((和focusmanager.setFocusedElement((

之间的差异

编辑

这是我从头开始启动VSTO加载项项目后发现的。它似乎是在Excel VSTO加载项中使用WPF的许多 Quirks之一,如果与创建的第一个WPF窗口一起使用,您将无法可靠地使Control.Focus()可靠地工作。p> workaround :创建一个虚拟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();
}

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

该领域似乎并未"完全"集中。光标在文本框中显示,但它并不闪烁,打字行不通!

您正在看到消息泵的影响不当:

带有WebBrowser Control的Excel CustomTaskPane-键盘/焦点问题

错误:不能在掉落浮动VSTO加载项

的datepicker上选择日期

vsto wpf contextMenu.menuitem单击" tastepane notedpane"

错误与对输入响应的控件以及在调度循环中错误过滤或重定向的void WndProc(ref Message m)消息有关。要解决它,您必须利用消息循环,例如:

protected override void WndProc(ref Message m)
{
  const int WM_PARENTNOTIFY = 528;
  if(m.Msg == WM_PARENTNOTIFY && !this.Focused)
  {
    this.Focus();
  }
  base.WndProc(ref m);
}

这类似于您在其他问题中引用的链接中 @emplemhost block鼠标事件的 @nian回答的内容。


这是Hans Passant的行为的摘要:

从来没有问题(即通常会出现问题(的是,您依靠Excel中的消息泵来派遣Windows消息,这些消息使这些控件响应输入。在WPF中,这与Winforms一样多,它们具有自己的调度循环,可以在传递到窗口之前过滤消息。当他们各自的调度员不使用各自的调度员时出现问题的关键事情是诸如Tabbing和short键击球。

,然后有些问题将通过在发送消息之前进行自己的过滤来引起这种问题。我猜想在一个反恶意软件功能上,微软永远担心程序会弄乱办公应用程序。

似乎您只需要设置WPF窗口的所有者即可。要完成工作,您需要使用对话框的WPF窗口对象初始化WindowInteropHelper。然后,您可以从Handle属性中获取WPF窗口的句柄(HWND(,并使用Owner属性指定WPF窗口的所有者。以下代码示例显示了在Win32应用程序中托管WPF对话框时如何使用WindowInteropHelper

  WindowInteropHelper wih = new WindowInteropHelper(myDialog);
  wih.Owner = ownerHwnd;
  myDialog.ShowDialog();

而不是:

var hwndOwner = (IntPtr)ExcelInterop.App.Hwnd;

尝试使用:

new WindowInteropHelper(window) { Owner = Process.GetCurrentProcess().MainWindowHandle };
window.ShowDialog();

您是否尝试过以下

1 。尝试将焦点设置在对话的Loaded Event内部。这将成为 对话窗口满载后的聚焦。

            private void MyWindow_Loaded(object sender, RoutedEventArgs e)
            {
                myTextBox.Focus();
            }

2 。尝试为您的控件设置键盘重点。

           Keyboard.Focus(myTextBox);

最新更新