2

私は.NET、具体的にはC#で作業しており、WebBrowserコントロールを含むWin Forms UserControlを作成しています。WebBrowser コントロールは、サードパーティの JavaScript コンポーネントを使用するページをホストします。私が抱えている問題は、javascript関数を呼び出してサードパーティのjavascriptコンポーネントを初期化し、コンポーネントが初期化されるまでWindowsフォームアプリケーションのUIをブロックすることです。これは、コンポーネントが内部javascriptイベントを介して通知します.

問題の一部は、サードパーティの JavaScript コンポーネントの構成パラメーターを変更する唯一の方法は、新しい構成で再初期化することです。たとえば、読み取り専用にしたい場合は、読み取り専用パラメーターで再初期化する必要があります。

Document.InvokeScript を呼び出し、Web ページで window.external を使用して UserControl メソッドを呼び出すことができるという点ですべてが機能していますが、私が抱えている問題は、呼び出しを行う UserControl コードをブロックする方法ですJavaScript コンポーネントを初期化して、JavaScript コンポーネントの初期化が完了するまで待機し、ユーザーに制御を返さないようにします。

このように動作させる必要がある理由は、フォームに「読み取り専用」チェックボックスがあり、UserControl の ReadOnly プロパティを変更して、JavaScript コンポーネントがデータを読み取り専用として表示し、ユーザーがクリックするかどうかを制御するためです。そのチェックボックスをすぐに使用すると、JavaScript エラーが発生するか、チェックボックスが JavaScript コンポーネントの実際の読み取り専用状態と同期しなくなります。これは、構成が変更された後、コントロールがまだ再初期化されておらず、すでに変更しようとしているために発生するようです。

AutoResetEvent から Application.DoEvents などのすべてを使用して機能させる方法を考え出すのに何時間も費やしましたが、機能させることができないようです。

私が見つけた最も近いのは、WebBrowserでスクリプトを呼び出し、実行が完了するのを待つ(同期される)ですが、VS2012で導入された機能を使用します(そして私はVS2010を使用しています)。 JavaScript イベントが発生するのを待っていないという点で少し異なります。

どんな助けでも大歓迎です。

4

1 に答える 1

1

そもそもの問題は、何らかのイベントが発生するまで UI スレッドを「ブロック」する必要があることです。通常、アプリケーションをリファクタリングして非同期イベント ハンドラー ( の有無にかかわらずasync/await) を使用し、実行制御をメッセージ ループに戻してブロックを回避することができます。

ここで、何らかの理由でコードをリファクタリングできないとしましょう。この場合、二次的なモーダル メッセージ ループが必要になります。また、厄介な再入シナリオを回避するために、イベントを待っている間はメイン UI を無効にする必要があります。待機自体は使いやすく (待機カーソルや進行アニメーションを使用するなど)、ビジーではないようにする必要があります ( DoEvents.

これを行う 1 つの方法は、目的の JavaScript イベント/コールバックが発生したときに自動的に閉じられる、ユーザー フレンドリーなメッセージを含むモーダル ダイアログを使用することです。完全な例を次に示します。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WbTest
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(IScripting))]
    public partial class MainForm : Form, IScripting
    {
        WebBrowser _webBrowser;
        Action _onScriptInitialized;

        public MainForm()
        {
            InitializeComponent();

            _webBrowser = new WebBrowser();
            _webBrowser.Dock = DockStyle.Fill;
            _webBrowser.ObjectForScripting = this;
            this.Controls.Add(_webBrowser);

            this.Shown += MainForm_Shown;
        }

        void MainForm_Shown(object sender, EventArgs e)
        {
            var dialog = new Form
            {
                Width = 100,
                Height = 50,
                StartPosition = FormStartPosition.CenterParent,
                ShowIcon = false,
                ShowInTaskbar = false,
                ControlBox = false,
                FormBorderStyle = FormBorderStyle.FixedSingle
            };
            dialog.Controls.Add(new Label { Text = "Please wait..." });

            dialog.Load += (_, __) => _webBrowser.DocumentText = 
                "<script>setTimeout(function() { window.external.OnScriptInitialized}, 2000)</script>";

            var canClose = false;
            dialog.FormClosing += (_, args) =>
                args.Cancel = !canClose;

            _onScriptInitialized = () => { canClose = true; dialog.Close(); };

            Application.UseWaitCursor = true;
            try
            {
                dialog.ShowDialog();
            }
            finally
            {
                Application.UseWaitCursor = false;
            }

            MessageBox.Show("Initialized!");
        }

        // IScripting
        public void OnScriptInitialized()
        {
            _onScriptInitialized();
        }
    }

    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IScripting
    {
        void OnScriptInitialized();
    }
}


次のようになります。


モーダル ダイアログで UI をブロックする


別のオプション (ユーザーフレンドリーではないもの) は、WaitOneAndPumpfrom hereのようなものを使用することです。メイン UI を無効にして、何らかの待機中のフィードバックをユーザーに表示することに注意する必要があります。


コメントに対処するために更新されました。WebBrowser は実際に UI の一部であり、ユーザーに表示されますか? ユーザーはそれと対話できる必要がありますか? その場合、セカンダリ スレッドを使用して JavaScript を実行することはできません。メイン スレッドで実行し、メッセージをポンピングし続ける必要がありますが、WaitOneほとんどの Windows メッセージをポンピングしません ( COM に関連するメッセージのごく一部のみをポンピングします)。上で紹介したものを使うことができるかもしれませんWaitOneAndPump。再入を避けるために、待機中は UI を無効にする必要があります。

とにかく、それはまだクラッジでしょう。線形のコード フローを維持するためだけに実行をブロックするべきではありません。を使用できない場合はasync/await、単純なステート マシン クラスをいつでも実装し、コールバックを使用して、残った場所から続行できます。以前はそうだっasync/awaitた。

于 2014-08-21T08:14:15.330 に答える