C#およびWebテクノロジーのクロスプラットフォームGUI

部分的に口頭の最初の製品仕様には、デスクトップ用のクロスプラットフォーム(Windows、Linux、Mac)クライアントとモバイルの軽量バージョン(Windows、Android、iPhone)の存在という要件が含まれていました。 可能であれば、インターフェイスは異なるOSにできるだけ類似している必要があります。
Monoのおかげでクロスプラットフォームアプリケーションを作成できますが、GUIの問題は未解決のままです。 .Net(Windows Forms、WPF)の既存のテクノロジーはWindowsでのみうまく機能し、Windows Formsを移植するという悲しい経験がありました。 Linuxでは、 GtkSharpを使用できますが、.Netを使用してWindowsにMonoをインストールするという考えは嫌です。 その結果、OSごとに個別のインターフェイスを作成および保守する必要があります。
この状況で.Netチーム(Webに偏りがある)は何を思い付くことができましたか? Webkitを埋め込み、html-js-cssバンドルにGUIを作成することにしました。
現在、このアプローチはWindowsで2年間、LinuxおよびMacで1年間正常に使用されています。 これまでのところ、手はモバイルプラットフォームに到達していません。


なんで?


すべてのプラットフォームで同一のインターフェース。 要素を表示するときにフォントをレンダリングする場合、わずかな違いのみが可能です。 後者は常にレイアウトエラーが原因です。
1つのOS向けの開発。 経験的に、Windowsで基本的な開発を行うだけで十分であり、他のプラットフォームではたまにしかチェックしないことを明らかにしました。 たとえば、リリース前。
Web開発のすべての力。 これは、チームがWeb開発者で構成されている場合に特に当てはまります。 html5、css3、使い慣れたアプローチとライブラリを使用できます。 ちなみに、Webアプリケーションの構築には一般的なフレームワークを使用しているため、jsインターフェイスしかありません。
フロントエンドとバックエンドへの分離。 プレゼンテーションとアプリケーションロジックを個別に開発し、APIを調整する機会があります。 たとえば、このインターフェイスは、ajaxリクエストを介して「サーバー」とやり取りする本格的なWebアプリケーションです。 デスクトップアプリケーションでは、これらの要求の処理をエミュレートします。 したがって、Chromeの開発者ツールを使用してインターフェースを開発およびデバッグし、必要な模擬応答をローカルサーバーに投げることができます。 特にdomやコンソールへのアクセスが必要な自信のある開発者は、デスクトップアプリケーションでfirebug liteを使用できます。
書きたいことがあります このような実験は、開発中に興奮を与え、プログラマーの過酷な日常生活を明るくします。

どうやって?


プラットフォームごとに、ネイティブアプリケーションを作成します。GUIは、1つのユーザーインターフェイス要素(ブラウザー)で構成され、ウィンドウ全体に拡張されています。
ブラウザーでhtmlを表示する方法を学習し、js-C#およびC#-js呼び出しを行う方法を見つける必要があります。 呼び出しの違いは奇妙に思えるかもしれませんが、簡単な説明があります-異なる機能が実装され、使用されているブラウザで動作します。

Mac OSX

ケシの下に何を埋め込むかは選択できません。 したがって、 MonoMacと標準ブラウザ使用します。 しかし、ライセンスには問題があります。 Monoなしでアプリケーションを自由に配布できます。 ユーザー自身がMonoをインストールする必要があるため、アプリケーションはAppStoreにアクセスできません。 Monoをアプリケーションに組み込む場合、 Xamarin.Macを購入する必要があります。Xamarin.Macの価格は、プログラマー1人の会社の規模に応じて300ドルまたは1000ドルです。
ケシの下で、最も簡潔なコードを得ました。 直感的でない唯一の場所は、jsからC#を呼び出すことです。
ブラウザーを初期化した後、jsがC#からコントローラーメソッドを呼び出すことができるオブジェクトを作成する必要があります。 インタラクションオブジェクトを呼び出します。
webView.WindowScriptObject.SetValueForKey(this, new NSString("interaction")); 

メソッドを定義し、jsから呼び出すことができるメソッドを示します。
  [Export("callFromJs")] public void CallFromJs(NSString message) { CallJs("showMessage", message + "   C#"); } [Export ("isSelectorExcludedFromWebScript:")] public static bool IsSelectorExcludedFromScript(MonoMac.ObjCRuntime.Selector sel) { if (sel.Name == "callFromJs") return false; return true; //      } 

これで、jsでcallFromJsメソッドを呼び出すことができます。
  window.interaction.callFromJs('  js.'); 

コメント付きの宣言された機能の完全なリスト
  public partial class MainWindowController : MonoMac.AppKit.NSWindowController { /*  */ //  xib(nib) ,      UI   public override void AwakeFromNib () { base.AwakeFromNib (); //     js    C#.   interaction // window.interaction.callFromJs(param1, param2, param3) -    js. webView.WindowScriptObject.SetValueForKey(this, new NSString("interaction")); webView.MainFrame.LoadHtmlString (@" <html> <head></head> <body id=body> <h1></h1> <button id=btn> C#</button> <p id=msg></p> <script> function buttonClick() { interaction.callFromJs('  js.'); } function showMessage(msg) { document.getElementById('msg').innerHTML = msg; } document.getElementById('btn').onclick = buttonClick; </script> </body> </html>", null); } //    ,       js [Export ("isSelectorExcludedFromWebScript:")] public static bool IsSelectorExcludedFromWebScript(MonoMac.ObjCRuntime.Selector aSelector) { if (aSelector.Name == "callFromJs") return false; return true; //      } [Export("callFromJs")] public void CallFromJs(NSString message) { CallJs("showMessage", new NSObject[] { new NSString(message + "   C#") }); } public void CallJs(string function, NSObject[] arguments) { this.InvokeOnMainThread(() => { webView.WindowScriptObject.CallWebScriptMethod(function, arguments); }); } } 


githubでの作業例
「コントローラーコードでWebViewへのリンクを追加する方法」を見つけたとき、このビデオを見逃しました。


Ubuntu

Monoでは、 webkit-sharpパッケージを使用します。
非直感的なコードの量は徐々に増加しています。
jsからC#を呼び出すために、参照によって遷移をインターセプトできます。
  browser.NavigationRequested += (sender, args) => { var url = new Uri(args.Request.Uri); if (url.Scheme != "mp") { //mp - myprotocol. //     . //        return; } var parameters = System.Web.HttpUtility.ParseQueryString(url.Query); handlers[url.Host.ToLower()](parameters); //    browser.StopLoading(); }; 

jsからの呼び出しは次のようになります。
  window.location.href = 'mp://callFromJs?msg=  js.'; 

別の方法は、TitleChangedイベントに関連付けられています。
jsで、ドキュメントのタイトルを設定します。
  document.title = JSON.stringify({ method: 'callFromJs', arguments: { msg: '  js'} }); 

C#では、TitleChangedイベントが発生し、タイトルを逆シリアル化し、以前のアプローチと同様に、ハンドラーを呼び出します。

考慮されるWebKitラッパーでは、任意のjsコードをC#から実行できるため、C#からjs呼び出しを実装できます。
  public void CallJs(string function, object[] args) { // javascript var js = string.Format(@" {0}.apply(window, {1}); ", function, new JavaScriptSerializer().Serialize(args)); Gtk.Application.Invoke(delegate { browser.ExecuteScript(js); }); } 

コメント付きの宣言された機能の完全なリスト
  public partial class MainWindow: Gtk.Window { private Dictionary<string, Action<NameValueCollection>> handlers; private WebView browser; public MainWindow (): base (Gtk.WindowType.Toplevel) { Build (); CreateBrowser (); this.ShowAll (); } protected void OnDeleteEvent (object sender, DeleteEventArgs a) { Application.Quit (); a.RetVal = true; } private void CreateBrowser () { //       js handlers = new Dictionary<string, Action<NameValueCollection>> { { "callfromjs", nv => CallJs("showMessage", new object[] { nv["msg"] + "   #" }) } }; browser = new WebView (); browser.NavigationRequested += (sender, args) => { var url = new Uri(args.Request.Uri); if (url.Scheme != "mp") { //mp - myprotocol. //     . //        return; } var parameters = System.Web.HttpUtility.ParseQueryString(url.Query); handlers[url.Host.ToLower()](parameters); //    browser.StopLoading(); }; browser.LoadHtmlString (@" <html> <head></head> <body id=body> <h1></h1> <button id=btn> C#</button> <p id=msg></p> <script> function buttonClick() { window.location.href = 'mp://callFromJs?msg=  js.'; } function showMessage(msg) { document.getElementById('msg').innerHTML = msg; } document.getElementById('btn').onclick = buttonClick; </script> </body> </html> ", null); this.Add (browser); } public void CallJs(string function, object[] args) { var js = string.Format(@" {0}.apply(window, {1}); ", function, new JavaScriptSerializer().Serialize(args)); Gtk.Application.Invoke(delegate { browser.ExecuteScript(js); }); } } 


githubでの作業例


Windowsでの主な開発を行っています。
詳細は1年前に同僚によってすでに説明されていましたが、この間、根本的な変更はありませんでした。 ある程度、これはアプローチの信頼性を示しています。 また、この記事には、1つのOSを例として使用することを検討するのに十分な詳細があります。
githubにを追加ます。

特徴

インターフェースを提示するこのような興味深い方法には、独自の特性があり、パスを繰り返す場合に注意する必要があります。
建設中の追加の時間消費。 インターフェイスをアプリケーションにリソースとして埋め込むための準備には時間がかかります:Saas、ボンディングファイル、縮小。 ただし、ブラウザでインターフェースを開発する場合、毎回インターフェースまたはアプリケーション自体を再構築する必要はありません。
増加したメモリ消費。 これは、このアプローチの唯一の重大なマイナス点です。 この場合、ブラウザは50メガバイトのRAMを消費します。 一方で、これはそれほど重要ではありませんが、対象となる視聴者が古い機器を使用している場合、この機能を考慮する必要があります。 別のテクノロジーに実装された同様のインターフェースがより少ないメモリを消費するかどうかは不明ですが。 いずれにせよ、ブラウザのメモリ消費量-ブラックボックスがあります。 他のシステム上の問題やパフォーマンスの低下に気付いていません。

Source: https://habr.com/ru/post/J204286/


All Articles