nos*_*tio 28 keyboard wpf focus webbrowser-control wpf-controls
显然,WPF WebBrowser控件存在一些严重的键盘和焦点问题.我整理了一个简单的WPF应用程序,只是一个WebBrowser和两个按钮.该应用程序加载一个非常基本的可编辑HTML标记(<body contentEditable='true'>some text</body>)并演示以下内容:
标签是行为不端的.用户需要按两次Tab键才能在WebBrowser中查看插入符号(文本光标)并能够键入.
当用户切换远离应用程序时(例如,使用Alt-Tab),然后返回,插入符号消失,她根本无法键入.需要物理鼠标单击WebBrowser的窗口客户端区域才能取回插入符号和击键.
不一致的是,一个虚线的焦点矩形出现在WebBrowser周围(当标签时,但不是点击时).我找不到摆脱它的方法(FocusVisualStyle="{x:Null}"没有帮助).
在内部,WebBrowser永远不会得到关注.对于逻辑焦点(FocusManager)和输入焦点(键盘)都是如此.将Keyboard.GotKeyboardFocusEvent和FocusManager.GotFocusEvent从不事件被解雇的web浏览器(虽然他们都对按钮做在同一个焦点范围).即使当插入符号是web浏览器内,FocusManager.GetFocusedElement(mainWindow)指向先前聚焦元件(按钮)和Keyboard.FocusedElement是null.同时,((IKeyboardInputSink)this.webBrowser).HasFocusWithin()回报true.
我会说,这样的行为几乎太功能无法实现,但这就是它的运作方式.我可能想出一些黑客来解决它并将其与原生WPF控件一起排成一行TextBox.我仍然希望,也许我在这里错过了一些模糊而简单的东西.有没有人处理过类似的问题?任何有关如何解决这个问题的建议将不胜感激.
此时,我倾向于为基于HwndHost的 WebBrowser ActiveX控件开发内部WPF包装器.我们还在考虑 WebBrowser的其他替代方案,例如Chromium Embedded Framework(CEF).
VS2012项目可以从这里下载,以防有人想玩它.
这是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>
Run Code Online (Sandbox Code Playgroud)
这是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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
[更新]如果我托管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>
Run Code Online (Sandbox Code Playgroud)
唯一的改进是我确实看到焦点事件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>");
Run Code Online (Sandbox Code Playgroud)
这也说明了焦点处理问题并非特定于contentEditable=true内容.
对于其他绊倒这篇文章并需要将键盘焦点设置到浏览器控件(不一定是控件中的特定元素)的人,这段代码对我有用。
首先,为添加一个项目参考(在VS中的Extensions下)Microsoft.mshtml。
接下来,只要您想使浏览器控件集中(例如,在加载Window时),只需“集中” HTML文档即可:
// Constructor
public MyWindow()
{
Loaded += (_, __) =>
{
((HTMLDocument) Browser.Document).focus();
};
}
Run Code Online (Sandbox Code Playgroud)
这会将键盘焦点放在Web浏览器控件内以及“不可见的” ActiveX窗口内,从而允许PgUp / PgDown之类的键在HTML页面上工作。
如果需要,您也许可以使用DOM选择在页面上找到特定元素,然后尝试找到focus()该特定元素。我自己还没有尝试过。
小智 5
它以这种方式运行的原因与它是一个ActiveX控件这一事实有关,它本身就是一个完全的Windows类(它处理鼠标和键盘的交互).实际上,在大多数情况下,当您看到使用的组件时,您会发现它是占用整个窗口的主要组件.它不必这样做,但它提出了问题.
这是一个讨论完全相同问题的论坛,通过阅读上一篇评论员的文章链接可以澄清其原因:
概述您遇到的问题
标签是行为不端的.用户需要按两次Tab键才能在WebBrowser中查看插入符号(文本光标)并能够键入.
那是因为浏览器控件本身就是一个可以选中的窗口.它不会立即将标签"转发"到它的子元素.
改变这种情况的一个方法是,以处理组件本身的WM消息,但请记住,这样做得到棘手,当你想它里面的"孩子"文件能够处理的消息.
请参阅:防止WebBrowser控件窃取焦点?特别是"答案".虽然他们的回答没有考虑,你可以控制通过设置静音性能组件是否通过与用户交互的对话框(可能会或可能不会在WPF控件存在...不知道)
当用户切换远离应用程序时(例如,使用Alt-Tab),然后返回,插入符号消失,她根本无法键入.需要物理鼠标单击WebBrowser的窗口客户端区域才能取回插入符号和击键.这是因为控件本身已经得到了关注.另一个考虑因素是添加代码来处理GotFocus事件,然后"改变"焦点所在的位置.棘手的部分是弄清楚这是"来自"文档 - >浏览器控件或你的应用程序 - >浏览器控件.我可以想到一些hacky方法来做到这一点(基于丢失焦点事件的变量引用,例如在gotfocus上检查)但没有任何尖叫优雅.
不一致的是,一个虚线的焦点矩形出现在WebBrowser周围(当标签时,但不是点击时).我找不到摆脱它的方法(FocusVisualStyle ="{x:Null}"没有帮助).我想知道改变Focusable是否会有所帮助或阻碍.从来没有尝试过,但我想冒昧地猜测,如果它确实有效,它将阻止它完全被键盘导航.
在内部,WebBrowser永远不会得到关注.对于逻辑焦点(FocusManager)和输入焦点(键盘)都是如此.该Keyboard.GotKeyboardFocusEvent和FocusManager.GotFocusEvent事件从未被解雇的web浏览器(虽然它们都在同一个焦点范围的按钮做的).即使插入符号在WebBrowser中,FocusManager.GetFocusedElement(mainWindow)指向先前聚焦的元素(按钮),而Keyboard.FocusedElement为null.同时,((IKeyboardInputSink)this.webBrowser).HasFocusWithin()返回true.人们已经遇到了两个浏览器控件都显示焦点(好吧......插入符号)或者甚至有一个隐藏控件关注焦点的问题.
总而言之,这是相当真棒,你可以与组件做什么,但它只是让您控制/更改与组预定义的行为以及行为是令人发狂的正确组合.
我的建议是尝试对消息进行子类化,这样你就可以直接通过代码引导焦点控制,并绕过它的窗口,试图这样做.
| 归档时间: |
|
| 查看次数: |
9886 次 |
| 最近记录: |