C#の通常のWindowsアプリケーションのUIとしてHTMLおよびWebBrowserコントロールを使用します。

ご存じのように、WebBrowserコントロールはInternet ExplorerのActiveXコンポーネントの単なるラッパーです。 したがって、すべての最新の機能を備えた本格的なレイアウトエンジンへのアクセスを提供します。 もしそうなら、通常のWindowsベースのアプリケーションのユーザーインターフェイスを作成するために、その根拠に基づいて(理由はわかりません)試してください。



もちろん、同じプロセスで(たとえばHttpListenerで)ミニWebサーバーを起動し、それを介して要求をキャッチすることも可能ですが、それは単純すぎて退屈でスポーツマンらしくないです。 楽しみのために、ネットワークコンポーネントなしでやってみましょう。



そのため、タスクは簡単です。HTMLフォームの送信をインターセプトし、POSTパラメーターに応じてアプリケーションロジックによって生成された新しいHTMLドキュメントを出力する必要があります。



まず、単一のコントロールが存在する単一のフォームを備えたWindowsフォームアプリケーションが必要です。これは、すべてのスペースを占有するWebBrowserです。



たとえば、インターフェイスの簡単なページを2つ描画します(簡潔にするために最小限のコンテンツを追加し、好みに合わせてスクリプトとスタイルを追加します)。



最初のページ:



<!DOCTYPE html> <html> <head><meta http-equiv="X-UA-Compatible" content="IE=11"></head> <body> <form method="post"> <input type="text" name="TEXT1" value="Some text" /> <input type="submit" value="Open page 2" name="page2" /> </form> </body> </html>
      
      





2ページ目:



 <!DOCTYPE html> <html> <head><meta http-equiv="X-UA-Compatible" content="IE=11"></head> <body> <div>%TEXT1%</div> <form method="post"> <input type="submit" value="Back to page 1" name="page1" /> </form> </body> </html>
      
      





注:X-UA-Compatibleは、一部のスタイルを正しく表示するために必要です。この例では使用されていませんが、問題があります。 詳細は説明しませんが、これがないと、コンポーネントは非常に古いものとの互換モードでページを描画し、その後のすべての結果をもたらします。



WebBrowserはいくつかのイベントを提供します。たとえば、Navigatingがあります。これは、フォームを送信する前またはリンクをたどる前に発生します。

ほとんど必要なものですが、このイベントは投稿パラメーターに関する情報を提供しません。 HTMLフォームにはaction属性がないため、GETパラメーターを使用できません。これにより、URLが空白になり、GETパラメーターに関する情報がなくなります。

リクエストに関するより詳細な情報を取得するには、ブラウザの内部COMオブジェクトでBeforeNavigate2イベント(詳細はこちら )をサブスクライブする必要があります。これは、ActiveXInstanceプロパティを通じて利用できるためです。

これを行う最も簡単な方法は、COMインターフェイスの宣言に煩わされないように、動的に行うことです。

デリゲートを宣言します。



 private delegate void BeforeNavigate2(object pDisp, string url, int Flags, string TargetFrameName, byte[] PostData, string Headers, ref bool Cancel);
      
      





次に、イベントをサブスクライブします(WebBrowserフォームのコンストラクターでは、ActiveXInstanceプロパティはnullになるため、ウィンドウが読み込まれた後、つまりOnLoadなどでこれを行うことをお勧めします):



 ((dynamic)webBrowser.ActiveXInstance).BeforeNavigate2 += new BeforeNavigate2(OnBeforeNavigate2);
      
      





そのため、PostDataには、&を介して結合されたkey = valueのペアで構成される文字列形式の投稿パラメーターがあります。 それらを分離して辞書に入れます:



 var parameters = (PostData == null ? string.Empty : Encoding.UTF8.GetString(PostData)) .Split('&') .Select(x => x.Split('=')) .Where(x => x.Length == 2) .ToDictionary(x => WebUtility.UrlDecode(x[0]), x => WebUtility.UrlDecode(x[1]));
      
      





さらに、このハンドラーでは、Cancelパラメーターを使用してアクションをキャンセルすることをお勧めします。これにより、再び空になります。



パラメーターを使用して、新しいページのテキストを生成します。 理論的には、特定のハンドラーマネージャーがここにねじ込まれ、いくつかのテンプレートに従って断片からページを収集するパラメーターに応じて必要なハンドラーを選択しますが、簡潔にするために、たとえば、ページpage1で最初のページを開き、page2ボタンで2番目のページ(名前マークアップでボタンを指定する必要があります。そうでない場合、どのボタンがポストパラメーターから押されたかを判別できません。また、括弧内のすべての行を次の名前のパラメーター値に置き換えます。



  private static string Handler(IReadOnlyDictionary<string, string> parameters) { // do some useful stuff here var newPage = "Not found"; if (parameters.ContainsKey(nameof(page1))) newPage = page1; if (parameters.ContainsKey(nameof(page2))) newPage = page2; parameters.ToList().ForEach(x => newPage = newPage.Replace("{" + x.Key + "}", x.Value)); return newPage; }
      
      





結果のHTMLテキスト付き文字列は、WebBrowserのDocumentTextプロパティに書き込まれます。

そして、このプロパティを設定するとOnBeforeNavigate2の新しい呼び出しがトリガーされるため、ページのリロードサイクルが無限になります。

DocumentTextのインストールが制御を返した後、メッセージ処理サイクルのどこかから呼び出されるため、このイベントの一時的なサブスクライブ解除は機能しません。

T.O. 最初の操作は処理する必要のあるユーザーアクションの結果としてフォームを送信することであり、2番目の操作は処理する必要のない結果の表示であるため、ハンドラーの2番目の呼び出しごとに常に無視する必要があります。 簡単にするために、OnBeforeNavigate2ハンドラーの開始時にbool変数を切り替えます。



 ignore = !ignore; if (ignore) return;
      
      







結果は次のとおりです。







最小限のアプリケーションに必要なのはこれだけです。 ただし、すべてがこのように機能するわけではありません。たとえば、入力タイプ= "ファイル"のファイルのデータを取得し、XMLHttpRequestを使用して、すべての種類のAJAXを含むスクリプトの正しい操作を行います。



ソースコード
 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Windows.Forms; namespace TestHtmlUI { internal sealed class MainForm : Form { private const string page1 = @"<!DOCTYPE html> <html> <head><meta http-equiv=""X-UA-Compatible"" content=""IE=11""></head> <body> <form method = ""post"" > <input type=""text"" name=""TEXT1"" value=""Some text"" /> <input type=""submit"" value=""Open page 2"" name=""" + nameof(page2) + @""" /> </form> </body> </html>"; private const string page2 = @"<!DOCTYPE html> <html> <head><meta http-equiv=""X-UA-Compatible"" content=""IE=11""></head> <body> <div>{TEXT1}</div> <form method=""post""> <input type=""submit"" value=""Back to page 1"" name=""" + nameof(page1) + @""" /> </form> </body> </html>"; private delegate void BeforeNavigate2(object pDisp, string url, int Flags, string TargetFrameName, byte[] PostData, string Headers, ref bool Cancel); private readonly WebBrowser webBrowser = new WebBrowser { Dock = DockStyle.Fill }; private bool ignore = true; private MainForm() { StartPosition = FormStartPosition.CenterScreen; Controls.Add(webBrowser); Load += (sender, e) => ((dynamic)webBrowser.ActiveXInstance).BeforeNavigate2 += new BeforeNavigate2(OnBeforeNavigate2); webBrowser.DocumentText = Handler(new Dictionary<string, string> { { nameof(page1), string.Empty } }); } private void OnBeforeNavigate2(object pDisp, string url, int Flags, string TargetFrameName, byte[] PostData, string Headers, ref bool Cancel) { ignore = !ignore; if (ignore) return; Cancel = true; var parameters = (PostData == null ? string.Empty : Encoding.UTF8.GetString(PostData)) .Split('&') .Select(x => x.Split('=')) .Where(x => x.Length == 2) .ToDictionary(x => WebUtility.UrlDecode(x[0]), x => WebUtility.UrlDecode(x[1])); webBrowser.DocumentText = Handler(parameters); } [STAThread] private static void Main() { Application.EnableVisualStyles(); Application.Run(new MainForm()); } private static string Handler(IReadOnlyDictionary<string, string> parameters) { var newPage = "Not found"; if (parameters.ContainsKey(nameof(page1))) newPage = page1; if (parameters.ContainsKey(nameof(page2))) newPage = page2; // do dome usefull stuff here parameters.ToList().ForEach(x => newPage = newPage.Replace("{" + x.Key + "}", x.Value)); return newPage; } } }
      
      








All Articles