wpf keyboard wpf-controls focus webbrowser-control
here

wpf - Teclado de control WebBrowser y comportamiento de enfoque.



keyboard wpf-controls (2)

La razón por la que se comporta de esta manera está relacionada con el hecho de que se trata de un control ActiveX, que en sí mismo es una clase completamente de Windows (maneja la interacción del mouse y el teclado). De hecho, la mayor parte del tiempo que ve el componente utilizado, encontrará que es el componente principal que ocupa una ventana completa debido a esto. No tiene que hacerse de esa manera, pero presenta problemas.

Aquí hay un foro en el que se discute exactamente el mismo problema y se pueden aclarar las causas leyendo los enlaces de los últimos artículos de los comentaristas:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/1b50fec6-6596-4c0a-9191-32cd059f18f7/focus-issues-with-systemwindowscontrolswebbrowser

Para describir los problemas que está teniendo

  • Tabular es mal comportamiento. El usuario debe presionar Tab dos veces para ver el cursor (cursor de texto) dentro de WebBrowser y poder escribir.

    eso se debe a que el control del navegador en sí mismo es una ventana a la que se puede tabular. No "reenvía" la pestaña a sus elementos secundarios inmediatamente.

    Una forma de cambiar esto sería manejar el mensaje WM para el componente en sí mismo, pero tenga en cuenta que hacerlo se vuelve complicado cuando se quiere que el documento "secundario" dentro de él pueda manejar mensajes.

    Ver: ¿ Impedir que el control WebBrowser robe el foco? específicamente la "respuesta". Aunque su respuesta no tiene en cuenta que puede controlar si el componente interactúa a través de diálogos con el usuario configurando la propiedad Silenciosa (puede o no existir en el control WPF ... no estoy seguro)

  • Cuando el usuario se aleja de la aplicación (por ejemplo, con Alt-Tab), luego retrocede, el caret desaparece y no puede escribir nada. Se requiere un clic físico del ratón en el área del cliente de la ventana de WebBrowser para recuperar el cursor y las pulsaciones del teclado. Esto es porque el control en sí ha recibido el foco. Otra consideración es agregar código para manejar el evento GotFocus y luego "cambiar" hacia donde se enfoca. La parte difícil es averiguar si esto era "del" documento -> control del navegador o su aplicación -> control del navegador. Puedo pensar en algunas maneras de hacer esto (una referencia variable basada en la pérdida del evento de enfoque verificada en gotfocus, por ejemplo) pero nada que parezca elegante.

  • De manera inconsistente, aparece un rectángulo de enfoque punteado alrededor de WebBrowser (al tabular, pero no al hacer clic). No pude encontrar una manera de deshacerme de él (FocusVisualStyle = "{x: Null}" no ayuda). Me pregunto si cambiar Focusable ayudaría o dificultaría. Nunca lo intenté, pero me aventuraré a suponer que, si funcionara, evitaría que el teclado fuera navegable.

  • Internamente, WebBrowser nunca recibe el foco. Eso es cierto tanto para el enfoque lógico (FocusManager) como para el enfoque de entrada (teclado). Los eventos Keyboard.GotKeyboardFocusEvent y FocusManager.GotFocusEvent nunca se activan para WebBrowser (aunque ambos lo hacen para botones en el mismo ámbito de enfoque). Incluso cuando el cursor está dentro de WebBrowser, FocusManager.GetFocusedElement (mainWindow) apunta a un elemento enfocado previamente (un botón) y Keyboard.FocusedElement es nulo. Al mismo tiempo, ((IKeyboardInputSink) this.webBrowser) .HasFocusWithin () devuelve true. La gente ha tenido problemas con el hecho de que 2 controles del navegador muestren el enfoque (bueno ... el caret) o incluso un control oculto que se centre.

En general, es bastante sorprendente lo que puede hacer con el componente, pero es la combinación correcta de permitirle controlar / cambiar el comportamiento junto con conjuntos predefinidos de comportamiento para estar enloquecedor.

Mi sugerencia sería tratar de subclasificar los mensajes para que pueda dirigir el control de enfoque directamente a través del código y evitar que la ventana intente hacerlo.

Aparentemente, hay algunos problemas graves de teclado y enfoque con el control WebBrowser de WPF . He creado una aplicación WPF trivial, solo un WebBrowser y dos botones. La aplicación carga un marcado HTML editable muy básico ( <body contentEditable=''true''>some text</body> ) y muestra lo siguiente:

  • Tabular es mal comportamiento. El usuario debe presionar Tab dos veces para ver el cursor (cursor de texto) dentro de WebBrowser y poder escribir.

  • Cuando el usuario se aleja de la aplicación (por ejemplo, con Alt-Tab), luego retrocede, el caret desaparece y no puede escribir nada. Se requiere un clic físico del ratón en el área del cliente de la ventana de WebBrowser para recuperar el cursor y las pulsaciones del teclado.

  • De manera inconsistente, aparece un rectángulo de enfoque punteado alrededor de WebBrowser (al tabular, pero no al hacer clic). No pude encontrar una manera de deshacerme de él ( FocusVisualStyle="{x:Null}" no ayuda).

  • Internamente, WebBrowser nunca recibe el foco. Eso es cierto tanto para el enfoque lógico ( FocusManager ) como para el enfoque de entrada ( Keyboard ). Los eventos Keyboard.GotKeyboardFocusEvent y FocusManager.GotFocusEvent nunca se activan para WebBrowser (aunque ambos lo hacen para botones en el mismo ámbito de enfoque ). Incluso cuando el cursor está dentro de WebBrowser, FocusManager.GetFocusedElement(mainWindow) apunta a un elemento enfocado previamente (un botón) y Keyboard.FocusedElement es null . Al mismo tiempo, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() devuelve true .

Yo diría que tal comportamiento es demasiado disfuncional para ser verdad, pero así es como funciona. Probablemente podría encontrar algunos hacks para arreglarlo y llevarlo a la fila con controles nativos de WPF como TextBox . Todavía espero, tal vez me esté perdiendo algo oscuro pero simple aquí. ¿Alguien ha lidiado con un problema similar? Cualquier sugerencia sobre cómo solucionar este problema sería muy apreciada.

En este punto, me inclino a desarrollar un envoltorio interno de WPF para el Control ActiveX de WebBrowser, basado en HwndHost . También estamos considerando otras alternativas a WebBrowser, como Chromium Embedded Framework (CEF).

El proyecto VS2012 se puede descargar desde here en caso de que alguien quiera jugar con él.

Esto es 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>

Este es el código C #, tiene un montón de trazas de diagnóstico para mostrar cómo se enrutan los eventos de enfoque / teclado y dónde se enfoca:

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; } } }

[ACTUALIZACIÓN] La situación no mejora si recibo WinForms WebBrowser (en lugar de, o lado a lado con 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>

La única mejora es que veo eventos de enfoque en WindowsFormsHost .

[ACTUALIZACIÓN] Un caso extremo: dos controles WebBrowser con dos marcas que muestran al mismo tiempo:

<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>");

Esto también ilustra que el problema del manejo del enfoque no es específico del contenido contentEditable=true .


Para cualquier otra persona que se haya topado con esta publicación y que necesite establecer el enfoque del teclado en el control del navegador (no necesariamente un elemento en particular dentro del control), este bit de código funcionó para mí.

Primero, agregue una referencia de proyecto (bajo Extensiones en VS) para Microsoft.mshtml .

A continuación, cuando desee enfocar el control del navegador (por ejemplo, cuando se carga la ventana), simplemente "enfoque" el documento HTML:

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

Esto colocará el foco del teclado dentro del control del navegador web y dentro de la ventana ActiveX "invisible", permitiendo que teclas como PgUp / PgDown funcionen en la página HTML.

Si lo desea, puede utilizar la selección de DOM para buscar un elemento en particular en la página e intentar focus() ese elemento en particular. No lo he intentado yo mismo.