Apparently, there are some serious keyboard and focus issues with WPF WebBrowser control. I've put together a trivial WPF app, just a WebBrowser and two buttons. The app loads a very basic editable HTML markup (some text
) and demonstrates the following:
显然,WPF WebBrowser控件存在一些严重的键盘和焦点问题。我整合了一个微不足道的WPF应用程序,一个网络浏览器和两个按钮。该应用程序加载一个非常基本的可编辑HTML标记(一些文本),并演示以下内容:
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
跨栏是行为不端。用户需要按Tab键两次,才能在WebBrowser中看到caret(文本光标),并能够键入。
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
当用户离开应用程序时(例如,使用Alt-Tab),然后返回,插入符号就消失了,她根本无法输入。要取回插入符号和击键,需要在WebBrowser的窗口客户端区域中单击一个物理鼠标。
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisual
does not help).
不一致的是,在WebBrowser周围会出现一个虚线焦点矩形(在制表时显示,但在单击时不显示)。我找不到摆脱它的方法(FocusVisual没有帮助)。
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent
and FocusManager.GotFocusEvent
events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow)
points to a previously focused element (a button) and Keyboard.FocusedElement
is null
. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin()
returns true
.
在内部,WebBrowser不会收到焦点。对于逻辑焦点(FocusManager)和输入焦点(键盘)都是如此。键盘。GotKeyboardFocusEvent FocusManager。GotFocusEvent事件不会因为WebBrowser而被触发(尽管它们都在同一个焦点范围内对按钮进行操作)。即使插入符号在WebBrowser中,FocusManager.GetFocusedElement(mainWindow)也指向一个先前聚焦的元素(一个按钮)和键盘。FocusedElement是null。同时,((IKeyboardInputSink)this.webBrowser). hasfocuswithin()返回true。
I'd say, such behaviour is almost too dysfunctional to be true, but that's how it works. I could probably come up with some hacks to fix it and bring it in row with native WPF controls like TextBox
. Still I hope, maybe I'm missing something obscure yet simple here. Has anyone dealt with a similar problem? Any suggestions on how to fix this would be greatly appreciated.
我得说,这种行为几乎是不正常的,不可能是真的,但它就是这样运作的。我可能会想出一些技巧来修复它,并将它与文本框之类的本地WPF控件放在一起。我仍然希望,也许我在这里漏掉了一些模糊但简单的东西。有人处理过类似的问题吗?对于如何解决这一问题,我们将非常感谢。
At this point, I'm inclined to develop an in-house WPF wrapper for WebBrowser ActiveX Control, based upon HwndHost. We are also considering other alternatives to WebBrowser, such as Chromium Embedded Framework (CEF).
在这一点上,我倾向于开发基于HwndHost的WebBrowser ActiveX控件的内部WPF包装器。我们还在考虑web浏览器的其他替代品,比如Chromium嵌入式框架(CEF)。
The VS2012 project can be downloaded from here in case someone wants to play with it.
VS2012项目可以从这里下载,以防有人想玩它。
This is XAML:
这是XAML:
This is C# code, it has a bunch of diagnostic traces to show how focus/keyboard events are routed and where the focus is:
这是c#代码,它有许多诊断跟踪,显示焦点/键盘事件是如何路由的,焦点在哪里:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
namespace WpfWebBrowserTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// watch these events for diagnostics
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.PreviewKeyDownEvent, new KeyEventHandler(MainWindow_PreviewKeyDown));
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(MainWindow_GotKeyboardFocus));
EventManager.RegisterClassHandler(typeof(UIElement), FocusManager.GotFocusEvent, new RoutedEventHandler(MainWindow_GotFocus));
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
// load the browser
this.webBrowser.NavigateToString("Line 1
Line 3
Line 3
");
this.btnLoad.IsChecked = true;
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
// close the form
if (MessageBox.Show("Close it?", this.Title, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
this.Close();
}
// Diagnostic events
void MainWindow_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_GotFocus(object sender, RoutedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
Debug.Print("{0}, key: {1}, source: {2}, {3}", FormatMethodName(), e.Key.ToString(), FormatType(e.Source), FormatFocused());
}
// Debug output formatting helpers
string FormatFocused()
{
// show current focus and keyboard focus
return String.Format("Focus: {0}, Keyboard focus: {1}, webBrowser.HasFocusWithin: {2}",
FormatType(FocusManager.GetFocusedElement(this)),
FormatType(Keyboard.FocusedElement),
((System.Windows.Interop.IKeyboardInputSink)this.webBrowser).HasFocusWithin());
}
string FormatType(object p)
{
string result = p != null ? String.Concat('*', p.GetType().Name, '*') : "null";
if (p == this.webBrowser )
result += "!!";
return result;
}
static string FormatMethodName()
{
return new StackTrace(true).GetFrame(1).GetMethod().Name;
}
}
}
[UPDATE] The situation doesn't get better if I host WinForms WebBrowser (in place of, or side-by-side with WPF WebBrowser):
[更新]如果我托管WinForms WebBrowser(代替WPF WebBrowser,或与WPF WebBrowser并行),情况不会变得更好:
The only improvement is that I do see focus events on WindowsFormsHost
.
唯一的改进是,我确实看到了WindowsFormsHost上的焦点事件。
[UPDATE] An extreme case: two WebBrowser controls with two carets showing at the same time:
[更新]一个极端的情况:两个WebBrowser控件同时显示两个carets:
this.webBrowser.NavigateToString("");
this.webBrowser2.NavigateToString("");
This also illustrates that the focus handling issue is not specific to cOntentEditable=true
content.
这也说明了焦点处理问题并不是针对cOntentEditable=true content的。
5
The reason it behaves this way is related to the fact that it's an ActiveX control which itself is a fully windows class (it handles mouse and keyboard interaction). In fact much of the time you see the component used you'll find it is the main component taking up a full window because of this. It doesn't have to be done that way but it presents issues.
它这样做的原因与它是一个ActiveX控件有关,它本身就是一个完整的windows类(它处理鼠标和键盘交互)。事实上,大多数时候你看到的组件,你会发现它是主组件占据了一个完整的窗口。不需要那样做,但它会带来问题。
Here's a forum discussing the exact same issue and it's causes can be clarified by reading the last commentators article links:
这里有一个讨论相同问题的论坛,其原因可以通过阅读最后的评论文章链接来澄清:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/1b50fec6-6596-4c0a-9191-32cd059f18f7/focus-issues-with-systemwindowscontrolswebbrowser
http://social.msdn.microsoft.com/forums/vstudio/en us/1b50fec6 - 6596 - 4 - c0a - 9191 - 32 - cd059f18f7/focus——问题————systemwindowscontrolswebbrowser
To outline the issues you're having
列出你所遇到的问题
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
跨栏是行为不端。用户需要按Tab键两次,才能在WebBrowser中看到caret(文本光标),并能够键入。
that's because the browser control itself is a window which can be tabbed to. It doesn't "forward" the tab to it's child elements immediately.
这是因为浏览器控件本身是一个窗口,可以对其进行制表。它不会立即将选项卡“转发”给它的子元素。
One way to change this would be to handle the WM message for the component itself but keep in mind that doing so gets tricky when you want the "child" document inside of it to be able to handle messages.
改变这一点的一种方法是处理组件本身的WM消息,但是请记住,当您希望组件内部的“子”文档能够处理消息时,这样做是很困难的。
See: Prevent WebBrowser control from stealing focus? specifically the "answer". Although their answer doesn't account that you can control whether the component interacts through dialogs with the user by setting the Silent property (may or may not exist in the WPF control... not sure)
注意:防止WebBrowser控件窃取焦点?特别是“回答”。尽管他们的答案并不说明您可以通过设置静默属性(WPF控件中可能存在也可能不存在)来控制组件是否通过对话框与用户交互…不确定)
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes. This is because the control itself has received the focus. Another consideration is to add code to handle the GotFocus event and to then "change" where the focus goes. Tricky part is figuring out if this was "from" the document -> browser control or your app -> browser control. I can think of a few hacky ways to do this (variable reference based on losing focus event checked on gotfocus for example) but nothing that screams elegant.
当用户离开应用程序时(例如,使用Alt-Tab),然后返回,插入符号就消失了,她根本无法输入。一个物理鼠标点击浏览器的窗口客户区,需要返回插入符号和击键。这是因为控制本身受到了关注。另一个考虑是添加代码来处理GotFocus事件,然后在焦点所在的位置进行“更改”。棘手的部分是搞清楚这是“从”文档->浏览器控件还是你的应用->浏览器控件。我可以想出一些简单易行的方法来实现这一点(例如,在gotfocus上检查焦点事件时,基于丢失焦点事件的变量引用),但是没有什么是优雅的。
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisual does not help). I wonder if changing Focusable would help or hinder. Never tried it but I'm going to venture a guess that if it did work it would stop it from being keyboard navigable at all.
不一致的是,在WebBrowser周围会出现一个虚线焦点矩形(在制表时显示,但在单击时不显示)。我找不到摆脱它的方法(FocusVisual没有帮助)。我想知道改变焦距是有益的还是有害的。我从来没有尝试过,但我敢打赌,如果它真的成功了,它就会阻止它成为键盘通航器。
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true. People have hit issues with where 2 browser controls both show the focus(well... the caret) or even had a hidden control take the focus.
在内部,WebBrowser不会收到焦点。对于逻辑焦点(FocusManager)和输入焦点(键盘)都是如此。键盘。GotKeyboardFocusEvent FocusManager。GotFocusEvent事件不会因为WebBrowser而被触发(尽管它们都在同一个焦点范围内对按钮进行操作)。即使插入符号在WebBrowser中,FocusManager.GetFocusedElement(mainWindow)也指向一个先前聚焦的元素(一个按钮)和键盘。FocusedElement是null。同时,(ikeyboarputsink)this.webBrowser . hasfocuswithin()返回true。人们遇到了两个浏览器控件同时显示焦点的问题。甚至有一个隐藏的控件来控制焦点。
All in all it's pretty awesome what you can do with the component but it's just the right mix of letting you control/change the behavior along with predefined sets of behavior to be maddening.
总之,你可以用这个组件做的非常棒,但是它只是让你控制/改变行为和预定义的行为集合的正确组合。
My suggestion would be to try to subclass the messages so you can direct the focus control directly through code and bypass it's window from trying to do so.
我的建议是尝试对消息进行子类化,这样您就可以通过代码直接指导焦点控件,并绕过它的窗口。
3
For anyone else stumbling upon this post and needing to set keyboard focus to the browser control (not a particular element within the control, necessarily), this bit of code worked for me.
对于任何偶然发现本文并需要将键盘焦点设置为浏览器控件(不一定是控件中的特定元素)的人来说,这段代码对我很有用。
First, add a project reference (under Extensions in VS) for Microsoft.mshtml
.
首先,为Microsoft.mshtml添加一个项目引用(在VS中扩展)。
Next, whenever you'd like to focus the browser control (say for example, when the Window loads), simply "focus" the HTML document:
接下来,每当您想要对浏览器控件进行焦点(例如,当窗口加载时)时,只需对HTML文档进行“焦点”:
// Constructor
public MyWindow()
{
Loaded += (_, __) =>
{
((HTMLDocument) Browser.Document).focus();
};
}
This will place keyboard focus inside the web browser control, and inside the "invisible" ActiveX window, allowing keys like PgUp / PgDown to work on the HTML page.
这将把键盘焦点放在web浏览器控件和“不可见的”ActiveX窗口中,允许像PgUp / PgDown这样的键在HTML页面上工作。
If you want to, you might be able to use DOM selection to find a particular element on the page, and try to focus()
that particular element. I have not tried this myself.
如果需要,您可以使用DOM selection在页面上找到一个特定的元素,并尝试将这个特定的元素集中在()上。我自己还没试过。