Teclado de control WebBrowser y comportamiento de enfoque.

Al parecer, hay algunos problemas graves de teclado y enfoque conControl de WPF WebBrowser. 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 demuestra lo siguiente:

Hacer tabulaciones es portarse mal. 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.

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) y el enfoque de entrada (Teclado). losKeyboard.GotKeyboardFocusEvent yFocusManager.GotFocusEvent los eventos nunca se activan para WebBrowser (aunque ambos lo hacen para los botonesen 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) yKeyboard.FocusedElement esnull. Al mismo tiempo,((IKeyboardInputSink)this.webBrowser).HasFocusWithin() devolucionestrue.

Yo diría que tal comportamiento es casi demasiado disfuncional para ser verdad, pero así es como funciona. Probablemente podría encontrar algunos trucos para arreglarlo y ponerlo en fila con controles nativos de WPF comoTextBox. 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 enHwndHost. Nosotros tambiénconsiderando otras alternativas para WebBrowser, como Chromium Embedded Framework (CEF).

El proyecto VS2012 se puede descargar desdeaquí 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;
        }

    }
}

[ACTUALIZAR] La situación no mejora si reciboWinForms WebBrowser (en lugar de, o al lado de 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 enWindowsFormsHost.

[ACTUALIZAR] Un caso extremo: dos controles WebBrowser con dos carets que se 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 de manejo de enfoque no es específico decontentEditable=true contenido.

Respuestas a la pregunta(2)

Su respuesta a la pregunta