29

どうやら、 WPF WebBrowser コントロールにはキーボードとフォーカスに関する重大な問題がいくつかあるようです。WebBrowser と 2 つのボタンだけの簡単な WPF アプリを作成しました。アプリは非常に基本的な編集可能な HTML マークアップ ( <body contentEditable='true'>some text</body>) を読み込み、次のことを示します。

  • タブ操作は不適切です。ユーザーは、Tab を 2 回押して WebBrowser 内のキャレット (テキスト カーソル) を表示し、入力できるようにする必要があります。

  • ユーザーがアプリから離れて (Alt-Tab などで) 切り替えてから戻ると、キャレットがなくなり、まったく入力できなくなります。キャレットとキーストロークを元に戻すには、WebBrowser のウィンドウ クライアント領域をマウスで物理的にクリックする必要があります。

  • 一貫性がなく、点線のフォーカス四角形が WebBrowser の周囲に表示されます (タブ移動時は表示されますが、クリック時は表示されません)。私はそれを取り除く方法を見つけることができませんでした (FocusVisualStyle="{x:Null}"助けにはなりません)。

  • 内部的には、WebBrowser がフォーカスを受け取ることはありません。これは、論理フォーカス ( FocusManager ) と入力フォーカス ( Keyboard )の両方に当てはまります。Keyboard.GotKeyboardFocusEventおよびFocusManager.GotFocusEventイベントは、WebBrowser に対して発生することはありません (ただし、どちらも同じフォーカス スコープ内のボタンに対して発生します)。キャレットが WebBrowser 内にある場合でも、以前にフォーカスされた要素 (ボタン)をFocusManager.GetFocusedElement(mainWindow)指し、. 同時に、 を返します。Keyboard.FocusedElementnull((IKeyboardInputSink)this.webBrowser).HasFocusWithin()true

私は、そのような行動はほとんど機能不全であり、真実ではないと言いますが、それがどのように機能するかです. 私はおそらくそれを修正するためのいくつかのハックを考え出し、TextBox. それでも、ここであいまいでありながら単純なものが欠けている可能性があることを願っています。誰かが同様の問題に対処しましたか?これを修正する方法についての提案は大歓迎です。

この時点で、 HwndHostに基づいて、WebBrowser ActiveX コントロール用の社内 WPF ラッパーを開発する傾向があります。また、Chromium Embedded Framework (CEF) など、WebBrowser に代わるものも検討しています。

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>

これは 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;
        }

    }
}

[更新] 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>

唯一の改善点は、フォーカス イベントが表示されることWindowsFormsHostです。

[更新]極端なケース: 2 つのキャレットを同時に表示する 2 つの 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ます。

4

2 に答える 2

7

この投稿に出くわし、ブラウザー コントロール (必ずしもコントロール内の特定の要素ではない) にキーボードフォーカスを設定する必要がある他の人にとって、このコードはうまくいきました。

まず、 のプロジェクト参照 (VS の拡張機能の下) を追加しMicrosoft.mshtmlます。

次に、ブラウザー コントロールをフォーカスしたい場合 (たとえば、ウィンドウが読み込まれたときなど) はいつでも、HTML ドキュメントを単に "フォーカス" します。

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

これにより、キーボード フォーカスが Web ブラウザー コントロール内および「非表示」の ActiveX ウィンドウ内に配置され、PgUp / PgDown などのキーが HTML ページで機能できるようになります。

必要に応じて、DOM 選択を使用してページ上の特定の要素を見つけ、focus()その特定の要素を試すことができる場合があります。私はこれを自分で試していません。

于 2015-02-19T20:06:30.177 に答える