wpf - WebBrowser 控制键盘和焦点行为

标签 wpf keyboard wpf-controls focus webbrowser-control

显然,WPF WebBrowser control 存在一些严重的键盘和焦点问题。 .我已经组装了一个简单的 WPF 应用程序,只有一个 WebBrowser 和两个按钮。该应用程序加载一个非常基本的可编辑 HTML 标记 ( <body contentEditable='true'>some text</body> ) 并演示以下内容:

  • 标签是行为不端。用户需要点击 Tab 两次才能看到 WebBrowser 中的插入符号(文本光标)并能够输入。
  • 当用户离开应用程序(例如,使用 Alt-Tab)然后返回时,插入符号消失了,她根本无法输入。需要物理鼠标单击 WebBrowser 的窗口客户区才能恢复插入符号和击键。
  • 不一致的是,一个虚线焦点矩形出现在 WebBrowser 周围(当 Tab 键时,而不是单击时)。我找不到摆脱它的方法( FocusVisualStyle="{x:Null}" 没有帮助)。
  • 在内部,WebBrowser 永远不会获得焦点。对于逻辑焦点 ( FocusManager ) 和输入焦点 ( Keyboard ) 都是如此。 Keyboard.GotKeyboardFocusEventFocusManager.GotFocusEvent事件永远不会为 WebBrowser 触发(尽管它们都为同一焦点范围内的按钮触发)。即使插入符号在 WebBrowser 中,FocusManager.GetFocusedElement(mainWindow)指向先前聚焦的元素(按钮)和 Keyboard.FocusedElementnull .同时,((IKeyboardInputSink)this.webBrowser).HasFocusWithin()返回 true .

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

    在这一点上,我倾向于基于 HwndHost 为 WebBrowser ActiveX 控件开发一个内部 WPF 包装器。 .我们也是 considering other alternatives 到 WebBrowser,例如 Chromium Embedded Framework (CEF)。

    VS2012项目可以从here下载以防有人想玩它。

    这是 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# 代码,它有一堆诊断跟踪来显示焦点/键盘事件是如何路由的以及焦点在哪里:
    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 并排使用):
    <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 上看到了焦点事件。 .

    [更新] 一个极端情况:同时显示两个插入符号的两个 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"/>
        <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。内容。

    最佳答案

    对于在这篇文章中绊倒并需要将键盘焦点设置到浏览器控件(不一定是控件中的特定元素)的其他任何人,这段代码对我有用。

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

    接下来,无论何时您想要聚焦浏览器控件(例如,当窗口加载时),只需“聚焦”HTML 文档:

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

    这会将键盘焦点置于 Web 浏览器控件内和“不可见”ActiveX 窗口内,允许 PgUp/PgDown 等键在 HTML 页面上工作。

    如果您愿意,您可以使用 DOM 选择来查找页面上的特定元素,然后尝试 focus()那个特定的元素。我自己没有尝试过。

    关于wpf - WebBrowser 控制键盘和焦点行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18256886/

    相关文章:

    c# - 将 DataGrid 绑定(bind)到 WPF 中的通用列表的最佳方法

    linux - 操作系统中的两个 I/O 阻塞进程和一个击键事件

    WPF 断边

    c# - wpf customcontrol,如何在itemscontrol DataTemplate中绑定(bind)按钮点击

    c# - 设置 IntervalBarSeries 的标签 (oxyplot)

    WPF DialogResult 声明性地?

    android - 如何在 EditText View 的焦点上默认使用字母键盘?

    c# - GotFocus 和 GotKeyboardFocus 的区别

    c# - 如何从服务器向客户端发送更新?

    iOS - 使用 CustomGestureRecognizer 设置键盘位置