WebKit .NETの使用について

はじめに



HTML + CSS + JavaScriptの組み合わせは、ユーザーインターフェイスを構築する普遍的な方法として確立されました。 また、Webアプリケーションだけでなく、デスクトップおよびモバイルアプリケーションでも同様です。 この例は、モバイルアプリケーションを作成するためのPhoneGapフレームワークであるWindows 8のメトロアプリケーションです。



HTML、CSS、およびJavaScriptを使用してインターフェイスを実装すると、主に、インターフェイスが一部のブラウザに表示されます。 デスクトップアプリケーションまたはモバイルアプリケーションを検討している場合は、明らかにブラウザーを埋め込む必要があります。



この記事では、WindowsのC#のデスクトップアプリケーションでWebKit .NETを使用することを検討します。





組み込みブラウザの要件



組み込みブラウザの基本的な要件を明らかにします。



まず、C#からHTMLドキュメントに任意のコンテンツを設定できるようにする必要があります-つまり、インターフェイスです。 さらに当然、C#とJavaScriptの間の通信が必要です。 JavaScriptからC#を呼び出し、C#からJavaScriptを呼び出すことができなければなりません。同期と非同期の2つの方法が必要です。 組み込みブラウザに実装されたインターフェースを備えたデスクトップアプリケーションは、多くの点で、クライアントサーバーアーキテクチャを備えた従来のWebアプリケーションと似ています。 したがって、JavaScriptから非同期C#呼び出しを行うことは非常に重要です。 これらの呼び出しは、従来のWebアプリケーションでのAJAX要求に類似しています。



Webインスペクターとデバッガーは、すべてのWeb開発者の親友です。 そして、これはどうですか? すぐに言ってみましょう-これはここではそれほど単純ではありません。 組み込みブラウザーで実行されているJavaScriptをデバッグする方法を見つけることができませんでしたが、domを調べる機会を見つけ、JavaScriptコンソールを入手しました。



したがって、WebKit .NETの基本的な要件は次のとおりです。





次に、上記のどれがWebKit .NETに存在し、欠落している機能がどのように実装されたかを検討します。



HTMLコンテンツの設定



WebKitBrowser.DocumentTextプロパティを使用すると、HTMLドキュメントに任意のコンテンツを設定できます 。 インターフェースは外部リソースから完全に独立しており、すべてのJavaScriptとCSSをHTMLに直接含めています。 生産性を高めるために、すべてのスクリプトは縮小された形式で含まれています。



C#からのJavaScript呼び出し



WebKit .NETのC#からのJavaScript呼び出しには、次のシグネチャを持つDocument.InvokeScriptMethodメソッドがあります。

public Object InvokeScriptMethod( string Method, params Object[] args )
      
      





残念ながら、このメソッドにはパラメーターをJavaScript関数に渡す際に問題があります-それは機能しません。



この問題を解決するには、C#から独自のJavaScript呼び出しプロトコルを開発する必要がありました。 アイデアは次のとおりです。







C#でJavaScript関数を呼び出すコードは次のとおりです。

 public object CallJS(string functionName, object[] arguments, bool async) { var dict = new Dictionary<string, object>(); dict["arguments"] = arguments; dict["functionName"] = functionName; dict["async"] = async; SetJsBuffer(dict); return webKitBrowser.Document.InvokeScriptMethod("JS_CALL_HANDLER"); } private void SetJsBuffer(object data) { string id = "cs-js-buffer"; Element elem = null; try { elem = webKitBrowser.Document.GetElementById(id); } catch (Exception) { } //  ,     if (elem == null) { elem = webKitBrowser.Document.CreateElement("div"); elem.SetAttribute("id", id); webKitBrowser.Document.GetElementsByTagName("body")[0].AppendChild(elem); } elem.TextContent = Newtonsoft.Json.JsonConvert.SerializeObject(data); }
      
      







JavaScriptでは、呼び出しは次のように処理されます。

 JS_CALL_HANDLER = function() { //       div var dataFromCSharp = getDataFromDOM("cs-js-buffer"); if (!dataFromCSharp) { return; } if (!dataFromCSharp.async) { return window[dataFromCSharp.functionName].apply( window, dataFromCSharp.arguments ); } else { Ext.Function.defer(function () { window[dataFromCSharp.functionName].apply( window, dataFromCSharp.arguments ); }, 1); } }
      
      







呼び出しを非同期にするには、指定された関数を単に呼び出します

1msの遅延。 値を返す非同期呼び出しは必要ないため、

そのようなソリューションは私たちを完全に満足させます。



JavaScriptからのC#呼び出し



基本的に、WebKit .NETにはJavaScriptからのネイティブC#呼び出しメカニズムはありません。 ドキュメントをじっと見ると、 WebKitBrowser.DocumentTitleChangedイベントが見つかりました。 これはおそらく、JavaScriptがdocument.titleを設定することでいつでも簡単に生成できる唯一のイベントです。



このイベントを魅力的にするものは2つあります。





これは、JavaScriptからのC#呼び出しプロトコルの基礎を形成しました。

次のリストは、C#を呼び出すためのJavaScriptコードを示しています。



 var callMap = {}; //   callback  /** * If call is synchronous, function returns response, received from c#, * otherwise - response data will be passed to callback. * * @param {Object} config Call properties * @param {String} config.name Name of C# function to call * @param {Object[]} config.arguments Array of arguments, that will be passed to C# function * @param {Boolean} config.async True, if this request must be performed asynchronously, * in this case callback and scope must be specified * @param {Function} config.callback Callback function * @param {Object} config.scope Scope for callback */ callCSharp = function(config) { var id = generateId(); //    var callObject = { id : id, name : config.name, arguments : config.arguments, async : config.async }; if (config.async) { callObject.callbackHandler = "COMMON_ASYNC_HANDLER"; callMap[id] = { callback: config.callback, scope : config.scope }; } // invoke C# by triggering WebKitBrowser.DocumentTitleChanged event document.title = Ext.encode(callObject); // elegant, isn't it! // execution continues only after all C# handlers will finish if (!config.async) { var csharpResponse = getDataFromDOM(id); return csharpResponse.response; } }
      
      







リストからわかるように、各コールには一意の識別子が提供されます。

次に、C#が結果を配置するdiv要素の識別子として使用されます。



呼び出しが同期的な場合、その完了の直前に、C#ハンドラーは

結果をHTMLドキュメントの本文に入れます。 この場合、callCSharpメソッドは

document.titleを設定した直後にdomから結果を抽出します。



非同期呼び出しの場合、C#は完了時にコールバック関数の起動を開始する必要があります

ハンドラー。 この場合、C#から次のリストに示す特別なJavaScriptメソッドを呼び出します。



 /** * Handler for all C# async requests. * It calls real callback according to passed id. * * C# provides JSON of special format in div with passed id: * { * response : {...}, * success : true or false * } * * response and success are passed to callback function * @param {String} id Id of C# call */ COMMON_ASYNC_HANDLER = function(id) { var dataFromCSharp = getDataFromDOM(id); var callbackParams = callMap[id]; delete callMap[id]; callbackParams.callback.apply(callbackParams.scope, [ dataFromCSharp.response, dataFromCSharp.success ]); }
      
      







C#側には、JavaScriptからの呼び出しへのサブスクリプションを制御するCallManagerクラスがあります。 CallManagerには、 WebKitBrowser.DocumentTitleプロパティの値を逆シリアル化する単一のWebKitBrowser.DocumentTitleChangedイベントハンドラーがあり、JavaScript名(config.name)に応じて、渡されたパラメーターで対応する登録済みハンドラーを呼び出します。 CallManagerは、同期または非同期のコールのタイプも考慮します。 タイプに応じて、ハンドラーは同期的または非同期的に呼び出されます。



Webインスペクター



インターフェースの開発は2段階で行います。 最初の段階では、ブラウザとWebサーバーを使用して、従来のWebアプリケーションとして開発します。 目的の結果が得られた場合にのみ、スクリプトのパッケージ化とC#との統合という第2段階に進みます。



最初の段階では、ブラウザーで開発者ツールを積極的に使用し、domとJavaScriptを完全に制御しました。 しかし、第2段階への移行後、インターフェイス全体が単純にブラックボックスに変わりました。 レイアウトとJavaScriptの問題を検出することが困難になりました。 Webインスペクターの少なくともいくつかの類似物を探す必要がありました。



残念ながら、WebKit .NETではネイティブWebKit Webインスペクターの使用が許可されておらず、ここではリモートデバッグはサポートされていません。 そのため、 Firebugに基づいた組み込みデバッガーであるFirebug Lite (1.4.0)を使用することにしました。



Firebug LiteのHTMLページで、次のように接続します。

 <!DOCTYPE html> <html> <head> ... <script type='text/javascript' src='/path/to/firebug-lite.js'> { overrideConsole: true, startInNewWindow: true, startOpened: true, enableTrace: true } </script> ... </head> <body>...</body> </html>
      
      







組み込みブラウザーを使用する場合、startinNewWindowオプションで設定された新しいウィンドウでFirebug Liteを開くとより便利です。 しかし、これを実現するには、C#でいくつかの操作が必要です。

 browser.Url = new Uri( "http://localhost/path/to/debug-version-of-interface.html", System.UriKind.Absolute); browser.NewWindowCreated += new NewWindowCreatedEventHandler( (sender, newWindowEventArgs) => { // create new form with single item - Firebug Lite window debuggerForm = new DebuggerForm(newWindowEventArgs.WebKitBrowser); debuggerForm.Show(); });
      
      







もちろん、Firebug Liteはスクリプトデバッグをサポートしていませんが、domを探索する機会を提供し、JavaScriptコンソールを提供します。これにより、開発が既に簡単になります。



おわりに



上記のすべての改善の後、WebKit .NETは安定して動作し、かなり大きなdomに対処する完全に使用可能な組み込みブラウザーになりました。



もちろん、この方法でのインターフェースの実装には特定の困難が伴います。これは主に本格的なWebインスペクターの欠如が原因ですが、プラスもあります。 たとえば、アプリケーションの他の部分のJavaScriptコードを再利用したり、モバイル、デスクトップ、Webアプリケーションに同じインターフェースを実装することもできます。 したがって、努力は正当であると考えることができます。



All Articles