2012年07月20日

WebKit.NETで簡素なWebブラウザを作る(C#)

前回はWebBrowserコントロールを使いましたが、今度はWebKit.NETを使ってみます。
WebkitはGoogle ChromeとかSafariとかAndroidとかで使われているレンダリングエンジンですね。これを.NETから簡単に使えるようにしたライブラリがWebKit.NETです。

C#でWebKit.NETを使う場合、インストールなどの事前準備の方が手間ですね。アプリケーションから使用する分にはWebBrowserコントロールを使う場合と大差はありません。

ファイルのダウンロード

WebKit.NETをダウンロードします。
WebKit .NET プロジェクト日本語トップページ - SourceForge.JP
執筆時点での最新版は「WebKit.NET-0.5-bin-cairo.zip」でした。
ダウンロードしたファイルは適当なフォルダに展開しておきます。

ツールボックスへ追加

Visual Studioの、ツールボックスで右クリックし、[アイテムの選択(I)...]をクリックします。
WebKit-Install-01.png
「.NET Frameworkコンポーネント」の[参照(B)...]をクリックして、展開したbinフォルダの中にあるWebKitBrowser.dllを選択します。
ツールボックスにWebKitBrowserが追加されました。
WebKit-Install-02.png

プロジェクトのプロパティ変更

プロジェクトのプロパティの「デバッグ」にある“Visual Studio ホスティング プロセスを有効にする(O)”のチェックを外します。
Visual Studioホスティングプロセスというのは、Visual Studioでデバッグ機能の改善・強化のためのものですが、WebKit.NETとは相性が悪いらしく、実行時にエラーになるようです。Visual Studioホスティングプロセスについては、下記のサイトなどを参照ください。
VSホスト・プロセス(*.vshost.exe)とは何か?[VS 2005のみ、C#、VB]

以上で、準備は終了です。

プログラムの作成とビルド

WebKitBrowserはWebBrowserとほとんど同じように使用できます。前回作成したプログラムのWebBrowserをWebKitBrowserに差し替えてみます。
Navigate,GoBack,GoForward,などはそのまま利用可能。RefreshはReloadに代わります。GoHomeは互換メソッドが見つかりませんでした。Webkitにはないのかも。

public Form1()
{
    InitializeComponent();

    this.comboBox1.Text = "http://www.google.com";
    this.button1.Text = "戻る";
    this.button2.Text = "進む";
    this.button3.Text = "更新";
    this.button4.Text = "ホーム";
    this.button5.Text = "移動";
}

/// <summary>
/// URL入力エリア:選択変更
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    try
    {
        // 移動
        webKitBrowser1.Navigate(comboBox1.Text);
    }
    catch
    {
    }
}

/// <summary>
/// URL入力エリア:キー入力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
    // エンターキー
    if (e.KeyCode == Keys.Enter)
    {
        // 移動ボタン入力と同じ
        button5.PerformClick();
    } 
}

/// <summary>
/// 戻るボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
    // 戻る
    this.webKitBrowser1.GoBack();
}

/// <summary>
/// 進むボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
    // 進む
    this.webKitBrowser1.GoForward();
}

/// <summary>
/// 更新ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
    // 更新(再読み込み)
    this.webKitBrowser1.Reload();
}

/// <summary>
/// ホームボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
    // ホームへ移動
}

/// <summary>
/// 移動ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
    // 入力したURLに移動する
    try
    {
        string url = comboBox1.Text;
        // "http://"が無ければ補完
        if (comboBox1.Text.StartsWith("http://") == false)
        {
            url = "http://" + url;
        }
        // 移動
        webKitBrowser1.Navigate(url);
        comboBox1.Items.Add(url);
    }
    catch
    {
    }
}

/// <summary>
/// WebKitブラウザ:ナビゲーション実行時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void webKitBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    // URL入力エリアにナビゲートしたサイトのURLを設定
    try
    {
        this.comboBox1.Text = this.webKitBrowser1.Url.ToString();
    }
    catch
    { 
    }
}

/// <summary>
/// WebKitブラウザ:ドキュメントのタイトル変更時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void webKitBrowser1_DocumentTitleChanged(object sender, EventArgs e)
{
    // フォームのタイトルを変更
    this.Text = this.webKitBrowser1.DocumentTitle;
}

ビルドしたあと、ビルドして出来た実行ファイルと同じフォルダに、Webkit.dllなどのdll群やリソースファイルをコピーします。具体的には、展開したbinフォルダ以下の内容をコピーすればOKです。

実行結果はこんな感じです。
WebKit-Install-03.png

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 21:00 | Comment(2) | TrackBack(0) | C#

2012年07月15日

WebBrowserコントロールで簡素なWebブラウザを作る(C#)

WebBrowserコントロールはWebブラウザとしての基本的な機能を持っているため、これを使うことで簡単なWebブラウザを作ることができます。(この手の情報はネット上に腐るほどあるんですが)

まず、新規プロジェクトを作成して、下記のコントロールを貼り付けます。 コントロール名称はデフォルトのままで。

  • WebBrowser(Webブラウザとしての中核)
  • ComboBox(URL入力用)
  • Button×5(「戻る」「進む」「更新」「ホーム」「移動」ボタン)

コードは下記のようになります。説明はコード中のコメントで。

public Form1()
{
    InitializeComponent();

    this.comboBox1.Text = "http://www.google.com";
    this.button1.Text = "戻る";
    this.button2.Text = "進む";
    this.button3.Text = "更新";
    this.button4.Text = "ホーム";
    this.button5.Text = "移動";
}

/// <summary>
/// URL入力エリア:選択変更
/// </summary>
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    try
    {
        // 移動
        webBrowser1.Navigate(comboBox1.Text);
    }
    catch
    {
    }
}

/// <summary>
/// Webブラウザ:ナビゲーション実行時
/// </summary>
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    // フォームのタイトルをナビゲートしたサイトのタイトルを設定
    this.Text = webBrowser1.Document.Title.ToString();
    // URL入力エリアにナビゲートしたサイトのURLを設定
    this.comboBox1.Text = webBrowser1.Url.ToString(); 
}

/// <summary>
/// 戻るボタン
/// </summary>
private void button1_Click(object sender, EventArgs e)
{
    // 戻る
    this.webBrowser1.GoBack();
}

/// <summary>
/// 進むボタン
/// </summary>
private void button2_Click(object sender, EventArgs e)
{
    // 進む
    this.webBrowser1.GoForward();
}

/// <summary>
/// 更新ボタン
/// </summary>
private void button3_Click(object sender, EventArgs e)
{
    // 更新(再読み込み)
    this.webBrowser1.Refresh();
}

/// <summary>
/// ホームボタン
/// </summary>
private void button4_Click(object sender, EventArgs e)
{
    // ホームへ移動
    this.webBrowser1.GoHome();
}

/// <summary>
/// 移動ボタン
/// </summary>
private void button5_Click(object sender, EventArgs e)
{
    // 入力したURLに移動する
    try
    {
        string url = comboBox1.Text;
        // "http://"が無ければ補完
        if (comboBox1.Text.StartsWith("http://") == false)
        {
            url = "http://" + url;
        }
        // 移動
        webBrowser1.Navigate(url);
        comboBox1.Items.Add(url);
    }
    catch
    {
    }
}

/// <summary>
/// URL入力エリア:キー入力
/// </summary>
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
    // エンターキー
    if (e.KeyCode == Keys.Enter)
    {
        // 移動ボタン入力と同じ
        button5.PerformClick();
    } 
}

/// <summary>
/// Webブラウザ:新しいウィンドウが開かれたとき
/// </summary>
private void webBrowser1_NewWindow(object sender, CancelEventArgs e)
{
    // キャンセル
    e.Cancel = true;

    // ここではキャンセルしているので新しいウィンドウは開かないし画面遷移もしない
    // 新しいウィンドウを開かずに内部のブラウザで開くためにはwebBrowserの改造が必要。
    // 今回は省略
    // [参照]
    // http://agrimo.jp/wp/?page_id=1059
    // http://vsug.jp/tabid/63/forumid/45/threadid/9273/scope/posts/Default.aspx
}

実行結果はこんな感じです。
webBrowser-Sample-01.png

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

タグ:C# WebBrowser
posted by among at 08:05 | Comment(0) | TrackBack(0) | C#

2011年09月19日

ListViewのオーナードローの問題(1)(C#メモ)

ListViewの詳細ビューで奇数行と偶数行を変えるような使い方を良く目にしますが、 これを実現するためにはオーナードローを使って自前で描画をしてやる必要があります。
これ自体はそれほど難しい事ではないです。下記のようにやれば実現出来ます。

// ListViewコントロール
public ListView listView1;

private void Form1_Load(object sender, EventArgs e)
{
    // ListViewを生成して配置する
    listView1 = new ListView();
    this.Controls.Add(listView1);

    // オーナードローの設定
    listView1.OwnerDraw = true;
    // Headerの表示をコントロールする為にイベントを追加
    listView1.DrawColumnHeader
        += new DrawListViewColumnHeaderEventHandler(
                    listView1_DrawColumnHeader);
    // Itemの表示をコントロールする為にイベントを追加
    listView1.DrawSubItem 
        += new DrawListViewSubItemEventHandler(
                    listView1_DrawSubItem);

    // プロパティの設定
    listView1.Dock = DockStyle.Fill; // 親(Form)全体に広げる
    listView1.View = View.Details;   // 詳細ビューを指定
    listView1.MultiSelect = false;   // 複数行の選択を禁止
    listView1.FullRowSelect = true;  // 項目を選択すると行全体を選択
    listView1.GridLines = true;      // グリッド線を表示
    
    // 列の作成
    listView1.Columns.Add("id", "ID", 120);
    listView1.Columns.Add("data", "データ", 120);

    // アイテムの追加
    for (int i = 1; i <= 10; i++)
    {
        string[] items = { i.ToString("d2"), "データ" + i.ToString("d2") };
        listView1.Items.Add(new ListViewItem(items));
    }
}

private void listView1_DrawSubItem(
                    object sender, 
                    DrawListViewSubItemEventArgs e)
{
    // 選択されているアイテムの描画
    if ((e.ItemState & ListViewItemStates.Selected) 
                    == ListViewItemStates.Selected)
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Highlight, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.HighlightText, 
                        e.Bounds);
        return;
    }

    // 奇数行は背景色を変更して描画
    if ((e.Item.Index % 2) != 0)
    {
        e.Graphics.FillRectangle(
                        Brushes.LightYellow, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }

    // 偶数行はデフォルト描画
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Window, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }
}

private void listView1_DrawColumnHeader(
                        object sender, 
                        DrawListViewColumnHeaderEventArgs e)
{
    // 描画はOSに任せる
    e.DrawDefault = true;
}

実行するとこのような感じになります。
listview-ownerdraw-1.png

ここまでは問題ないのですが、ここでツールヒントを使うように設定を変更してみます。

// Form1_Loadに下記の行を追加
listView1.ShowItemToolTips = true;   // ツールヒント表示

すると、選択された行の描画が上手くいかなくなります。
具体的には、選択された行にマウスカーソルを置くと、選択が解除されたような感じになります。

listview-ownerdraw-2.png

原因を調べてみると、どうやらDrawSubItemに渡される引数DrawListViewSubItemEventArgsのItemStateにSelectedのビットがONになっていないケースがあるようです。

DrawSubItemでItemStateをリストに描画するように改造します。

private void listView1_DrawSubItem(
                        object sender, 
                        DrawListViewSubItemEventArgs e)
{
    // 選択されているアイテムの描画
    if ((e.ItemState & ListViewItemStates.Selected) 
                    == ListViewItemStates.Selected)
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Highlight, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.HighlightText, 
                        e.Bounds);
        return;
    }

    // 奇数行は背景色を変更して描画
    if ((e.Item.Index % 2) != 0)
    {
        e.Graphics.FillRectangle(
                        Brushes.LightYellow, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }

    // 偶数行はデフォルト描画
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Window, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }
}

実行してみると、選択行は「Selected, Focused, Hot, ShowKeyboardCues」となりますが、 カーソルを上に置いてしばらくすると「0」になってしまいます。

listview-ownerdraw-3.png

なぜ、このようなことになるのか、調べてみたのですが良く分かりませんでした。 おそらくは、ツールヒントを表示する処理の中で呼び出されているのだと思いますが…。

結局、回避する方法は分からず、ツールヒントを使用しないぐらいしか対応策は考えつきませんでした。

ListViewの詳細ビューのオーナードローには、他にも問題(というか知らないとトラップにはまること)があるので、別の機会に書きたいと思います。
(実はMSDNにちゃんと書いてあるかもしれませんが…気づいてないだけで。)

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 21:44 | Comment(0) | TrackBack(0) | C#