Delphi で WebBrowser を使う

ある意味 FAQ である Delphi で WebBrowser を使う方法です。VCL アプリではなく .NET アプリ on C# Builder なのが多少違う部分ではありますが。

まず Windows フォームアプリケーション - C#Builder のプロジェクトを作ります。既存のものがあればそれを開くだけでもOKです。次ツールパレット上で右クリックして「インストール済みの .NET コンポーネント」を選択します。しばらく経つとウィンドウが表示されます。ActiveX コンポーネントのタブをクリックして、一覧の中にある Microsoft Web Browser にチェックを入れて OK ボタンを押します。

これでツールパレットの ActiveX カテゴリの中に WebBrowser コンポーネントが現れますので、これを通常通りフォームに貼り付けるだけでブラウザが作れます。素晴らしい!

VCL には TWebBrowser がいつのまにか標準装備されていますね。Delphi6 Personal の時はやっぱり同じような方法でコンポーネントを追加した記憶があるのですが。

同じ方法でいろいろなコンポーネントを追加出来るので、暇があって興味のある方は遊んでみましょう。

マウスジェスチャ対応ブラウザコンポーネント 暫定版

とりあえず載せてみます。突っ込み大歓迎!

#region マウスジェスチャ対応ブラウザクラス
/// <summary>
/// マウスジェスチャ対応ブラウザクラス
/// </summary>
public class MGWebBrowser : WebBrowser
{
    // マウスジェスチャ 方向
    private enum GestureDirection
    {
        Up,
        Down,
        Left,
        Right,
        None,
    }

    // マウスジェスチャ 状態
    private enum GestureStatus
    {
        Gesturing,
        Gestured,
        GestureStop,
        None,
    }

    private bool mouseGestureLeftDown = false;
    private bool mouseGestureRightDown = false;

    private Point mouseStartPos;
    private Point mousePreStartPos;

    private GestureStatus mouseGestureStatus = GestureStatus.None;

    private int mouseGestureAccuracy = 10; // マウス移動量感度
    //private int mouseGectureClearance = 7; // マウス移動間隔 未使用
    private List<GestureDirection> gestureList;

    public MGWebBrowser()
    {
        gestureList = new List<GestureDirection>();
        this.Navigated += new WebBrowserNavigatedEventHandler(MGWebBrowser_Navigated);
    }

    void MGWebBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
    {
        this.Document.MouseDown += new HtmlElementEventHandler(Document_MouseDown);
        this.Document.MouseUp += new HtmlElementEventHandler(Document_MouseUp);
        this.Document.MouseMove += new HtmlElementEventHandler(Document_MouseMove);
    }

    void Document_MouseMove(object sender, HtmlElementEventArgs e)
    {
        if (mouseGestureStatus != GestureStatus.None)
        {
            int X = e.MousePosition.X - mouseStartPos.X;
            int Y = e.MousePosition.Y - mouseStartPos.Y;
            int pX = e.MousePosition.X - mousePreStartPos.X;
            int pY = e.MousePosition.Y - mousePreStartPos.Y;

            // 移動量が mouseGestureAccuracy 以上であれば方向データを記録
            if (Math.Abs(X) >= mouseGestureAccuracy || Math.Abs(Y) >= mouseGestureAccuracy)
            {
                if (gestureList.Count == 0 || getDirection(pX, pY) != gestureList[gestureList.Count - 1])
                {
                    gestureList.Add(getDirection(X, Y));
                    mouseStartPos.X = e.MousePosition.X;
                    mouseStartPos.Y = e.MousePosition.Y;
                    mousePreStartPos.X = e.MousePosition.X;
                    mousePreStartPos.Y = e.MousePosition.Y;
                }
            }
        }
    }

    void Document_MouseUp(object sender, HtmlElementEventArgs e)
    {
        Debug.WriteLine("Mouse UP");
        Debug.WriteLine(e.MouseButtonsPressed);
        if (e.MouseButtonsPressed == (MouseButtons.Left | MouseButtons.Right))
        {
            Debug.WriteLine("Gesturing Off");
        }
        else
        {
            if (e.MouseButtonsPressed == MouseButtons.Right)
            {
                mouseGestureRightDown = false;
            }
            else if (e.MouseButtonsPressed == MouseButtons.Left)
            {
                mouseGestureLeftDown = false;
            }
        }

        if (mouseGestureStatus == GestureStatus.Gesturing)
        {
            if (gestureList[0] == GestureDirection.Left)
            {
                GoBack();
            }
            else if (gestureList[0] == GestureDirection.Right)
            {
                GoForward();
            }
            mouseGestureStatus = GestureStatus.None;
            gestureList.Clear();
        }
    }

    void Document_MouseDown(object sender, HtmlElementEventArgs e)
    {
        if (e.MouseButtonsPressed == MouseButtons.Left)
        {
            mouseGestureLeftDown = true;
        }
        else if (e.MouseButtonsPressed == MouseButtons.Right)
        {
            mouseGestureStatus = GestureStatus.Gesturing;

            mouseStartPos.X = e.MousePosition.X;
            mouseStartPos.Y = e.MousePosition.Y;
            mousePreStartPos.X = e.MousePosition.X;
            mousePreStartPos.Y = e.MousePosition.Y;

            mouseGestureRightDown = true;
        }
        else if (e.MouseButtonsPressed == (MouseButtons.Left | MouseButtons.Right))
        {
            Debug.WriteLine("Gesturing On");
            mouseGestureStatus = GestureStatus.Gesturing;
            e.ReturnValue = true;
            if (mouseGestureRightDown)
            {
                this.GoBack();
            }
            else if (mouseGestureLeftDown)
            {
                this.GoForward();
            }
        }
    }

    private GestureDirection getDirection (int X, int Y)
    {
        if (Math.Abs(X) >= Math.Abs(Y))
        {
            if (X > 0)
            {
                return GestureDirection.Right;
            }
            else
            {
                return GestureDirection.Left;
            }
        }
        else
        {
            if (Y > 0)
            {
                return GestureDirection.Down;
            }
            else
            {
                return GestureDirection.Up;
            }
        }
    }
}
#endregion

ジェスチャする度に gestureList に方向データを溜めていきます。んで右ボタンから指を離す時に gestureList の内容を判断して、対応する動作を行います。今は戻ると進むしか実装していませんが、追加するのは簡単です。まぁ最近の流れからいえば、カスタマイズが可能なように作るべきでしょうから、デリゲート使いまくりで作っていくことになると思いますが。

ポップアップが出る問題は解決していません。あと Generics を使ってる List 型を使っているので C#2.0 専用です。

マウスジェスチャ対応ブラウザコンポーネント

を探しています(おい

軽く調べたところ無かったので「勉強を兼ねてやってみるか」ということで作り始めてるんですけどね。これが思うように行かなくてちょっと困っていたります。

通常ジェスチャはマウスの右ボタンを押した状態で行うのですが、ジェスチャ終了後ボタンから指を離す時に IE コンポーネント標準の右クリックイベントが反応してしまい、見慣れたポップアップメニューが出てしまって全然美しくないです。これをなんとかしたいのですが…。

今までの経験からすると、イベント用メソッドに渡されてきたオブジェクトをごにょごにょすると抑制出来たりするはずなのですが、それっぽいプロパティ等が見当たりません。見当たらないので片っ端から適当な値を入れてみたりしてますが、当然結果は駄目。何故だー!

と、いろいろいじくっていたのですが、この時に大変なことに気付いてしまいました。VC#2005 でブレークポイントを設定してデバッグしていたのですが、ブレークしてる時に変数の中身とかが見れますよね?なんと VC#2005 はその場で変数の中身を書き換えられるのです! 変数の中身が確認出来るだけでも十分便利なのに、書き換えまで可能とは…。恐るべし VS2005!! マイクロソフトの気合いの程が伺えます。本気で Professional 買いたくなりました。というか、絶対買います!

話がそれてしまいましたので元に戻しますと、今のところ機能としてはそれなりの動作をしている、しかしポップアップメニューが出てしまうので美しくないマウスジェスチャですが、一通り完成したらここで公開してみたいと思います。まだまだ道のりは遠そうですが…。

Dock Anchor vs Align

やっぱり Delphi の Align と同じ動作をさせるのは難しいみたいですね。コードの中でコントロールを生成してレイアウトしていく場合、DockStyle.Fill は問答無用で描写範囲一杯に表示しやがります。なんとか抑制させたいといろいろやってみたのですが、よーわからん。なんでフォームデザイナで置いたコントロールだとうまく行くんだろう。不思議。解決策は TableLayoutPanel を使う方法ですか。面倒だなぁ。なんとか良い方法を考えてみたいと思います。

Visual C# 2005 Express Edition Beta

インストールしてしまいました。夢にまで見た C#2.0 環境です(大げさ)。今まで Delphi しか使ってなかったので比較出来ませんでしたが、すげーよー。まずエディタが Delphi2005 のものより断然良いです。支援機能も強力で、

button1.Click +=

ここまで書いてタブキーを押すと

button1.Click += new EventHandler(button1_Click);

を補完し、更にタブキーを押すと

void button1_Click(object sender, EventArgs e)
{
    throw new Exception("The method or operation is not implemented.");
}

ここまで作ってくれます!凄い!自作イベントでも作ってくれるのかどうかまでは分かりませんが(期待しても良いですか?)、ここまでやってくれるならエディタのみでサクサク書けますね。それにベータとは言え、かなり安定しているように感じます。Delphi2005 より安定してるかも…。もっと頑張れ Borland!!

んで、早速以前から作りたかったアプリケーションの作成に取り掛かっているのですが、2.0 特有の機能を使おうかどうか多少迷っていたりします。使わなくても作れるなら互換性を重視して使わずにやってみようかと思ってますが、魅力的なコンポーネントが結構あるので心が揺らぎます…。

ちなみに VC#2005 インストール後、Delphi2005 のヘルプが BDS から呼び出せなくなってしまいました。スタートメニューからなら起動させることは出来るんですけど F1 で呼び出せないのはとても痛い…。

歴史に残るグランプリ

書かずにはいられない。もちろん F1 についてである。悪い意味で歴史に名を残す最悪のグランプリになってしまった。

アメリカGP:レース フェラーリが今季初優勝! ミシュランに異例の事態(F1-Live.com)

スタート直前に各車がピットロードに入っていき、頭からガレージに車を入れていく様子を見て胸が痛くなった。その中で行われた 6 台のみのレース。もちろん観客からはブーイングの嵐。あまりに酷すぎる仕打ちに対しての当然の行為である(モノを投げ込むとか危険極まりない行為は論外だが)。

いろいろな意味で波紋を投げかけるレースとなったのは間違い無い。が、あんなシーンはもう二度と見たくない。

日々性能の上がる F1 に対して、レギュレーションの変更という締め付けで速度を抑える方法を取ってきた FIA。速すぎるマシンは時に悲劇の事故を誘発する。この事故を防止するという意味での FIA の現在までの行動は十分納得出来る。しかし、今回のミシュランのタイヤ交換を認めなかった頑固とも言える FIA の姿勢にはどんな理由があったのだろうか。素人目で見る限り、ブリヂストン勢が拒否したのでもない限りタイヤ交換には問題無いと思える。例外を作りたくない等の理由はあるのかもしれない。しかし、それはドライバーの安全を犠牲にしてでも守らないといけないものなのだろうか。いや、そんなはずはない。ドライバーの安全が第一、この原則は何よりも尊重されるべき事柄である。ミシュラン勢もそう考えたからこそ全車リタイアという決断をしたのだ。

次回グランプリまで、この事件は数々の議論を巻き起こすだろう。しばらく様子を見守りたい。そして、是非良い方向で解決して欲しいと切に願う。

ということで

ちょっと文体を変えて書いてみました。イヤ、ありえないでしょ、あれは。今まで殆ど欠かさず見てきましたが、あんな光景は見たことがありません。終わったことにぐちぐち言っても仕方有りませんので、なんとか解決して欲しいですね。

市民になりました

ど〜ん!!

というか、はてなダイアリー市民って何やねん!と思ったのは内緒です。何でもキーワードの登録とか、投票が出来るようになるそうな。なるほど、こうやってキーワードは登録されていく訳か。どうやって登録するのか不思議だったので、長年の謎が一つ解決いたしました。

市民になったお祝いなのか関東地方は先ほど地震が…。怖い怖い!

もう一つお祝いなのかどうか分かりませんが、何故かここのページが livedoor MOVIESからリンクされてます(20日 AM2:30現在。そろそろ消えるかも)。映○関連Blog *1 というカテゴリに入ってますが、そんなジャンルの日記だったとは書いてる本人ですら初耳です!ちょっと書いただけで、内容なんてさっぱり無かったのに…。大変申し訳無い気持ちで一杯です^^; アクセスアップを狙っていろんなキーワードを意図的に悪用したことは認めますが^^;;;

記念に意味無く初めて画像も登録してみます。さ、F1でも見て寝るかな。

*1:伏字にしてあるのは、キーワードに反応してまた載ってしまわないようにする為の措置です。ご了承ください。