C#でお手軽ハイブリッドアプリケーション (1)

前書き

2015年を「プログラミング」という点で見ると、個人的には「Electronがやたら騒がれた」という印象を受けます。

qiita.com

自分でも上記など試してみましたが、「JavaScript関連だけ経験がある人がデスクトップアプリケーションを作る」ためには、お手軽な環境といえるのでしょう。それに、Node.jsなどのライブラリが使えれば非常に便利、という場合もありますよね。

他の言語、開発環境に慣れた人でも、各種ライブラリとは別の要素として、「XAMLだのFXMLだのよりも、一般的なCSSで多様なデザインができる」「表示中心のアプリケーションはHTML+CSSだと便利」という側面もあると思います。

また、スマートフォン向けではハイブリッドアプリケーションも選択肢になって久しいところです。

furoshiki.hatenadiary.jp

目標

これらの前提を踏まえつつ、ここでは、(Node.jsとかは無視して)シンプルなハイブリッドアプリケーションをWindows限定で書く場合、について示します。

C#もNuGetなりで色々なライブラリもありますから、デザイン面をHTML/CSS?JavaScriptにできるだけでメリットがある場合に適用できるでしょう。

とりあえず枠を用意する

Visual Studioを開き、新規プロジェクトで「新規Windows Formsアプリケーション」を選択、出てきたフォームに、WebBrowser, MenuStrip, ContextMenuStripを貼り付けます。フォームのサイズは、適当に広げます。

f:id:mokake:20151229131927p:plain

え?2015年も終わるのにWinFormsはない?いえいえ、Windows Formsも少しずつHiDPI対応が進んでいますよ。それに、WinFormsなら(WebBrowserは無理でも)Monoでも動作する(場合もあります)よ。それに、そもそもこれは単なる枠です。アプリケーション本体はHTML/CSS/JavaScriptで作るので、枠はさっさとWinFormsで作ってしまえばいいんです。WPFでもWebBrowser貼り付けるだけですから。

上記の画面は、(PCが遅いので軽く動作する)VS2010ですが、まあどれでも似たようなものでしょう。デザイン画面でメニューが隠れてしまうのが難点ですが……。

とりあえずHTMLを準備する

次はHTMLファイルを準備しましょう。

適当な場所(C#側から指定が必要なので、なるだけ簡単なpathがいいと思います)に、HTMLをおきます。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>テスト</title>
</head>
<body onload='
  document.getElementById("box").textContent =
    "[Browser] " + window.navigator.userAgent +
    " --- [Version] " + window.navigator.appVersion;
'>
<h1>テスト</h1>
<div id="box"></div>
</body>
</html>

保存はUTF-8でお願いしますね。

単に見出し「テスト」と、ブラウザの種別・バージョンを表示するだけのものです。

お手元のIEに投げ込んでみてください。下に実行可否を尋ねる領域が出てくるかもしれませんが、とりあえず表示されるだろうと思います。

……もしブラウザ種別などが出てこなかったら、それは業務用か業務アプリ開発用の、IEが古~いものですね(ブラウザ種別の表示に使う"textContent"はIE8以前では動きません)。

Can I use Node.textContent

WebBrowserでHTMLを表示する

このHTMLファイルのフルパスを、Form1のLoadイベントで設定します。C#は@をつけると文字列や正規表現リテラルが書きやすいので便利ですよね。

private void Form1_Load(object sender, EventArgs e)
{
    webBrowser1.Url = new Uri(@"c:\html\test.html");
}

あとは、これで実行。……すると「テスト」しか出ないと思います。そこで、先のHTMLの「textContent」を「innerHTML」に変えてみてください。

f:id:mokake:20151229134136p:plain

MSIE 7.0」「Trident/7.0」って、これIE7じゃないですか。そう、「WebBrowser IE バージョン」とかで検索するとわかりますが、WebBrowserコントロールは、(WinFormsでもWPFでも)IE7として動作します。

このままjQuery 1.xなど入れてがんばる!という場合は、そのままでいいと思いますが、マイクロソフトが「古いIEはサポート終わり」と言う中、普通ならIEも最新にしますよね。Vistaでない限り、IEは11になるはずです。なら、WebBrowserだってIE11として動作してほしいところ。

実はIEも9や10あたりで、かなりWeb標準に近づいてはいるんです(先のcaniuse.comで調べると、よくわかります)。

IEのバージョンを変更する

検索すると、metaタグでできる説もあるのですが、手元ではうまくいきませんでした。なので、仕方ないけどレジストリを変更します。といってもユーザレベルの項目なので、管理者権限は不要です。

OsadaSoft » [C#] WebBrowserコントロールのレンダリングモードをデフォルトのIE7から最新IEモードに変更する

[C#] WebBrowser コントロールで使われている Internet Explorerを最新のバージョンに変更する

上の2つを組み合わせてみます(レジストリ項目や設定値はiPentec、設定コードの流れはOsadaSoftを参考に)。

public static bool SetBrowserVersion(WebBrowser browser, string appName, int version=-1, bool useStandard=false)
{
    if (version < 7) { version = browser.Version.Major; }

    bool result = false;

    var keyName1 = @"Software\Microsoft\Internet Explorer\Main\";
    var keyName2 = @"FeatureControl\FEATURE_BROWSER_EMULATION";
    var keyName = keyName1 + keyName2;

    var hostedName = appName.Substring(0, appName.Length - 4) + ".vshost.exe";

    int value = version * 1000;
    if (useStandard)
    {
        if (version > 9) { value += 1; }
        if (version > 7) { value += version * 111; }
    }

    using (var reg = Registry.CurrentUser.CreateSubKey(keyName))
    {
        reg.SetValue(appName, value, RegistryValueKind.DWord);
        reg.SetValue(hostedName, value, RegistryValueKind.DWord);
        result = true;
    }

    return result;
}

やや高機能なメソッドにしてみました。

  • IEのバージョンを指定可能
  • 標準モードとIEモードを指定可能
  • バージョン無指定だと最新
  • Visual Studioから実行した場合("vshost.exe"になる)でも適用

もっと簡単な指定も、ラッパーを被せれば簡単にできるんじゃないでしょうか。

これで最新IE(IE11)にした場合、次のような表示になると思います。

f:id:mokake:20151229135737p:plain

無事、IE11のUA名が表示されました。

……本当はC#側とJavaScript側の相互作用とか入れるつもりでしたが、もう時間切れ。続きも年内に書きたい……ですね、はい。