Klawiatura sterująca WebBrowser i zachowanie fokusa
Najwyraźniej są pewne poważne problemy z klawiaturą i skupieniemKontrola WPF WebBrowser. Przygotowałem trywialną aplikację WPF, po prostu WebBrowser i dwa przyciski. Aplikacja ładuje bardzo prosty edytowalny znacznik HTML (<body contentEditable='true'>some text</body>
) i pokazuje, co następuje:
Tabbing jest niewłaściwy. Użytkownik musi dwukrotnie nacisnąć Tab, aby zobaczyć kursor (kursor tekstowy) wewnątrz WebBrowser i móc pisać.
Gdy użytkownik przełącza się z aplikacji (np. Za pomocą Alt-Tab), następnie wraca, karetka zniknęła, a ona nie może w ogóle pisać. Fizyczne kliknięcie myszką w obszarze roboczym okna WebBrowser jest wymagane, aby odzyskać kursor i naciśnięcia klawiszy.
Niespójnie, wokół WebBrowser pojawia się prostokąt z przerywaną ostrością (podczas zakładania, ale nie po kliknięciu). Nie mogłem znaleźć sposobu, aby się go pozbyć (FocusVisualStyle="{x:Null}"
nie pomaga).
Wewnętrznie WebBrowser nigdy nie otrzymuje fokusu. To prawda zarówno dla logicznego skupienia (FocusManager) i fokus wejściowy (Klawiatura). TheKeyboard.GotKeyboardFocusEvent
iFocusManager.GotFocusEvent
zdarzenia nigdy nie zostaną wystrzelone do WebBrowser (chociaż oba dotyczą przycisków)w tym samym zakresie ostrości). Nawet gdy daszek jest w WebBrowser,FocusManager.GetFocusedElement(mainWindow)
wskazuje na wcześniej skupiony element (przycisk) iKeyboard.FocusedElement
jestnull
. W tym samym czasie,((IKeyboardInputSink)this.webBrowser).HasFocusWithin()
zwracatrue
.
Powiedziałbym, że takie zachowanie jest prawie zbyt dysfunkcyjne, aby mogło być prawdziwe, ale tak to działa. Prawdopodobnie mógłbym wymyślić kilka hacków, aby to naprawić i ustawić w wierszu z rodzimymi kontrolkami WPF takimi jakTextBox
. Nadal mam nadzieję, że może brakuje mi tutaj czegoś niezrozumiałego, ale prostego. Czy ktoś zajmował się podobnym problemem? Wszelkie sugestie, jak to naprawić, byłyby bardzo mile widziane.
W tym momencie jestem skłonny opracować wewnętrzny wrapper WPF dla WebBrowser ActiveX ControlHwndHost. My też jesteśmyrozważając inne alternatywy do WebBrowser, takiego jak Chromium Embedded Framework (CEF).
Projekt VS2012 można pobrać ztutaj w przypadku, gdy ktoś chce się nim bawić.
To jest 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>
To jest kod C #, ma on kilka śladów diagnostycznych, aby pokazać, w jaki sposób zdarzenia fokusa / klawiatury są kierowane i gdzie skupia się:
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;
}
}
}
[AKTUALIZACJA] Sytuacja nie poprawia się, gdy go przyjmujęWinForms WebBrowser (zamiast lub obok siebie z 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>
Jedynym ulepszeniem jest to, że widzę zdarzenia fokusoweWindowsFormsHost
.
[AKTUALIZACJA] Ekstremalny przypadek: dwie kontrolki WebBrowser z dwoma karnetami wyświetlanymi w tym samym czasie:
<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>");
Pokazuje to również, że problem z obsługą fokusa nie jest specyficznycontentEditable=true
zawartość.