WebBrowser 控制键盘和焦点行为 [英] WebBrowser control keyboard and focus behavior

查看:51
本文介绍了WebBrowser 控制键盘和焦点行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

显然,WPF WebBrowser 控件.我已经组合了一个简单的 WPF 应用程序,只有一个 WebBrowser 和两个按钮.该应用会加载一个非常基本的可编辑 HTML 标记(<body contentEditable='true'>some text</body>)并演示以下内容:

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 (<body contentEditable='true'>some text</body>) and demonstrates the following:

  • Tabbing 行为不端.用户需要按两次 Tab 才能看到 WebBrowser 中的插入符号(文本光标)并能够输入.

  • Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.

当用户离开应用程序(例如,使用 Alt-Tab)然后返回时,插入符号消失了,她根本无法打字.需要物理鼠标单击 WebBrowser 的窗口客户区才能取回插入符号和击键.

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.

不一致的是,WebBrowser 周围会显示一个带点的焦点矩形(在选项卡时,但在单击时不显示).我找不到摆脱它的方法(FocusVisualStyle="{x:Null}" 没有帮助).

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 (FocusVisualStyle="{x:Null}" does not help).

在内部,WebBrowser 永远不会获得焦点.这对于两个逻辑焦点都是如此 (FocusManager) 和输入焦点(键盘).Keyboard.GotKeyboardFocusEventFocusManager.GotFocusEvent 事件永远不会为 WebBrowser 触发(尽管它们都为按钮在同一焦点范围内触发).即使插入符号位于 WebBrowser 内,FocusManager.GetFocusedElement(mainWindow) 也会指向先前聚焦的元素(按钮),并且 Keyboard.FocusedElementnull.同时,((IKeyboardInputSink)this.webBrowser).HasFocusWithin() 返回true.

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.

我想说,这种行为几乎是功能失调的,不可能是真的,但这就是它的工作原理.我可能会想出一些技巧来修复它,并将它与原生 WPF 控件(如 TextBox)结合在一起.我仍然希望,也许我在这里遗漏了一些晦涩但简单的东西.有没有人处理过类似的问题?任何有关如何解决此问题的建议将不胜感激.

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.

此时,我倾向于为 WebBrowser ActiveX 控件开发一个内部 WPF 包装器,基于 HwndHost.我们也在考虑其他WebBrowser 的替代品,例如 Chromium Embedded Framework (CEF).

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).

VS2012 项目可以从这里 以防有人想玩它.

The VS2012 project can be downloaded from here in case someone wants to play with it.

这是 XAML:

<Window x:Class="WpfWebBrowserTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Width="640" Height="480" Background="LightGray">

    <StackPanel Margin="20,20,20,20">
        <ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>

        <WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="300"/>

        <Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
    </StackPanel>

</Window>

这是 C# 代码,它有一堆诊断跟踪来显示焦点/键盘事件是如何路由的以及焦点在哪里:

This is C# code, it has a bunch of diagnostic traces to show how focus/keyboard events are routed and where the focus is:

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("<body contentEditable='true' onload='focus()'>Line 1<br>Line 3<br>Line 3<br></body>");
            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;
        }

    }
}

[更新]如果我托管 WinForms WebBrowser(代替 WPF WebBrowser 或与 WPF WebBrowser 并排):

[UPDATE] The situation doesn't get better if I host WinForms WebBrowser (in place of, or side-by-side with WPF WebBrowser):

<StackPanel Margin="20,20,20,20">
    <ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>

    <WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>

    <WindowsFormsHost Name="wfHost" Focusable="True" Height="150" Margin="10,10,10,10">
        <wf:WebBrowser x:Name="wfWebBrowser" />
    </WindowsFormsHost>

    <Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>

唯一的改进是我确实在 WindowsFormsHost 上看到了焦点事件.

The only improvement is that I do see focus events on WindowsFormsHost.

[UPDATE] 一个极端情况:两个 WebBrowser 控件同时显示两个插入符号:

[UPDATE] An extreme case: two WebBrowser controls with two carets showing at the same time:

<StackPanel Margin="20,20,20,20">
    <ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>

    <WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
    <WebBrowser Name="webBrowser2" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>

    <Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>

this.webBrowser.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text</textarea></body>");
this.webBrowser2.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text2</textarea></body>");

这也说明焦点处理问题并非特定于 contentEditable=true 内容.

This also illustrates that the focus handling issue is not specific to contentEditable=true content.

推荐答案

对于其他偶然发现这篇文章并需要将 keyboard 焦点设置到浏览器控件(不是控件中的特定元素,必然),这段代码对我有用.

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.

首先,为 Microsoft.mshtml 添加一个项目引用(在 VS 中的 Extensions 下).

First, add a project reference (under Extensions in VS) for Microsoft.mshtml.

接下来,当您想要集中浏览器控件时(例如,当窗口加载时),只需集中"HTML 文档即可:

Next, whenever you'd like to focus the browser control (say for example, when the Window loads), simply "focus" the HTML document:

// Constructor
public MyWindow()
{
    Loaded += (_, __) =>
    {
        ((HTMLDocument) Browser.Document).focus();
    };
}

这会将键盘焦点置于 Web 浏览器控件内,以及不可见"的 ActiveX 窗口内,从而允许 PgUp/PgDown 等键在 HTML 页面上工作.

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.

如果您愿意,您可以使用 DOM 选择来查找页面上的特定元素,并尝试 focus() 该特定元素.我自己没试过.

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.

这篇关于WebBrowser 控制键盘和焦点行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆