По-видимому, есть серьезные проблемы с клавиатурой и фокусом с WPF WebBrowser control. Я собрал тривиальное приложение WPF, просто WebBrowser и две кнопки. Приложение загружает очень базовую редактируемую разметку HTML (<body contentEditable='true'>some text</body>
) и демонстрирует следующее:
-
Вкладка не работает. Пользователю нужно дважды нажать клавишу Tab, чтобы увидеть курсор (текстовый курсор) внутри WebBrowser и иметь возможность печатать.
-
Когда пользователь отключается от приложения (например, с помощью Alt-Tab), затем возвращается назад, каретка исчезает, и она не может набрать вообще. Физический щелчок мышью в области клиента окна WebBrowser необходим, чтобы вернуть каретку и нажатия клавиш.
-
Несомненно, прямоугольник с точками фокусировки отображается вокруг WebBrowser (при табуляции, но не при нажатии). Я не мог найти способ избавиться от него (
FocusVisualStyle="{x:Null}"
не помогает). -
Внутри WebBrowser никогда не получает фокуса. Это верно для обоих логических фокусов (FocusManager) и фокус ввода (Keyboard). События
Keyboard.GotKeyboardFocusEvent
иFocusManager.GotFocusEvent
никогда не запускаются для WebBrowser (хотя оба они делают для кнопок в той же области фокуса). Даже если каретка находится внутри WebBrowser,FocusManager.GetFocusedElement(mainWindow)
указывает на ранее сфокусированный элемент (кнопка), аKeyboard.FocusedElement
-null
. В то же время((IKeyboardInputSink)this.webBrowser).HasFocusWithin()
возвращаетtrue
.
Я бы сказал, что такое поведение почти слишком дисфункционально, чтобы быть правдой, но как это работает. Возможно, я мог бы придумать некоторые хаки, чтобы исправить это и привести его в ряд с встроенными элементами управления WPF, например TextBox
. Тем не менее, я надеюсь, может быть, я пропустил что-то неясное, но все же простое здесь. Кто-нибудь имел дело с подобной проблемой? Любые предложения о том, как исправить это, будут очень признательны.
На этом этапе я склонен разрабатывать внутреннюю оболочку WPF для WebBrowser ActiveX Control на основе HwndHost. Мы также рассматриваем другие альтернативы для 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>
Это код С#, он содержит множество диагностических трасс, чтобы показать, как маршрутизируются события фокуса/клавиатуры и где находится фокус:
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;
}
}
}
[UPDATE] Ситуация не улучшается, если я размещаю WinForms 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
.