第25章 オーナードロー・メニュー


この章では、オーナードローという手法を用いてメニュー項目にイメージを表示してみます。



Win32APIによるプログラミングでは、オーナードローは結構面倒くさい部類に入っていました。しかし、C#では簡単です。

メニュー項目をオーナードローするには、

1.MenuItemオブジェクトのOwnerDrawプロパティをtrueに設定する。
2.MeasureItemイベントの処理
3.DrawItemイベントでの描画
たったこれだけです。

MeasureItemイベントを処理するには、これにMeasureItemEventHandlerデリゲートを関連づけます。

MeasureItem += new MeasureItemEventHandler(miCat_MeasureItem);
miCat_MeasureItem自作メソッドの第2引数にはMeasureItemEventArgsオブジェクトがきます。これにより、描画する範囲を指定できます。

MeasureItemEventArgsクラスは、まだやっていないリストボックス、コンボボックス、チェックボックスや、メニュー項目の各コントロールのMeasureItemイベントのデータを提供します。

このクラスのプロパティは4つあります。

プロパティプロパティ値意味
GraphicsGraphics計測対象となるGraphicsオブジェクトの取得
Indexint計測が必要な項目インデックスの取得
ItemHeightint項目の高さの取得・設定
ItemWidthint項目の幅の取得・設定

DrawItemイベントを処理するには、これにDrawItemEventHandlerデリゲートを関連づけます。

DrawItem += new DrawItemEventHandler(miCat_DrawItem);
miCat_DrawItemの自作メソッドの第2引数は、DrawItemEventArgs型となります。

DrawItemEventArgsクラスは、DrawItemイベントのデータを提供します。

このクラスには7つのプロパティがあります。

プロパティプロパティ値 意味
BackColorColor項目の背景色の取得
BoundsRectangle描画項目の境界を表す四角形の取得
FontFont項目に割り当てられているフォントの取得
ForeColorColor項目の前景色の取得
GraphicsGraphics描画するGraphicsの取得
Indexint描画されている項目のインデックスの取得
StateDrawItemState描画項目の状態の取得

DrawItemState列挙体のメンバと意味は次の通りです。

メンバ意味
Checked項目がチェックされている(メニューのみ)
ComboBoxEdit項目は、コンボボックスの編集部分
Default項目は規定の状態
Disabled項目は使用できない
Focus項目にフォーカスがある
Grayed項目が灰色(メニューのみ)
HotLight項目はホットトラッキング中
Inactive項目はアクティブでない
NoAccelerator項目がキーボードアクセラレータなしで表示される
NoFocusRect項目は、フォーカスがあることを示す四角形なしで表示される
None項目には状態がない
Selected項目が選択されている

DrawItemEventArgsクラスのDrawBackground メソッドは、背景を描画します。 また、DrawFocusRectangleメソッドは、フォーカスを示す四角形を描画します。

さて、これだけの予備知識があれば、オーバードローメニューを作ることができます。 では、サンプルを見てみましょう。

まず、前準備としてcat.gifという画像ファイルを、リソースとして埋め込んでおきます。 背景を、透明処理してあるとなおよいです。

// contextmenu03.cs

using System;
using System.Drawing;
using System.Windows.Forms;

class contextmenu03
{
    public static void Main()
    {
        MyForm mf = new MyForm();
        Application.Run(mf);
    }
}

class MyForm : Form
{
    Bitmap bmpCat;

    public MyForm()
    {
        Text = "猫でもわかるC#";
        BackColor = SystemColors.Window;

        bmpCat = new Bitmap(GetType(), "contextmenu03.cat.gif");
       

        ContextMenu cm = new ContextMenu();
        ContextMenu = cm;

        MenuItem miFile = new MenuItem();
        miFile.Text = "ファイル(&F)";
        cm.MenuItems.Add(miFile);

        MenuItem miExit = new MenuItem("終了(&X)");
        miExit.Click += new EventHandler(miExit_Click);
        miFile.MenuItems.Add(miExit);

        MenuItem miCat = new MenuItem();
        miCat.OwnerDraw = true;
        miCat.MeasureItem += new MeasureItemEventHandler(miCat_MeasureItem);
        miCat.DrawItem += new DrawItemEventHandler(miCat_DrawItem);
        miCat.Click += new EventHandler(miCat_Click);
        miFile.MenuItems.Add(miCat);

        MenuItem miOpen = new MenuItem("開く(&O)");
        miOpen.Click += new EventHandler(miOpen_Click);
        miFile.MenuItems.Add(miOpen);
    }

    void miExit_Click(object sender, EventArgs e)
    {
        Close();
    }

    void miOpen_Click(object sender, EventArgs e)
    {
        MessageBox.Show("「開く」が選択されました",
            "猫C#",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }

    void miCat_MeasureItem(object sender, MeasureItemEventArgs e)
    {
        e.ItemWidth = bmpCat.Width;
        e.ItemHeight = bmpCat.Height;
    }

    void miCat_DrawItem(object sender, DrawItemEventArgs e)
    {
        Rectangle rc = e.Bounds;
        rc.X = (rc.Width - bmpCat.Width) / 2;
        rc.Width = bmpCat.Width;

        Graphics g = e.Graphics;

        e.DrawBackground();
        g.DrawImage(bmpCat, rc);
    }

    void miCat_Click(object sender, EventArgs e)
    {
        MessageBox.Show("猫がクリックされました",
            "猫C#",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }
}
まずは、MyFormクラスを見てみましょう。これは、Formクラスから継承されています。

また、bmpCatというインスタンスフィールドを持っています。

このクラスの、コンストラクタでは、まずText,BackColorプロパティを設定しています。

次に、リソースから、cat.gifを読み出して、Bitmapオブジェクトを生成し、bmpCatに参照を代入しています。

あとは、コンテキストメニューを作っています。

miCatを作るときのみ、ちょっと注意してください。このメニュー項目は、オーナードローします。

MenuItem miCat = new MenuItem();
miCat.OwnerDraw = true;
miCat.MeasureItem += new MeasureItemEventHandler(miCat_MeasureItem);
miCat.DrawItem += new DrawItemEventHandler(miCat_DrawItem);
miCat.Click += new EventHandler(miCat_Click);
miFile.MenuItems.Add(miCat);
OwnerDrawプロパティをtrueに設定しています。

MeasureItem, DrawItemイベントにたいして、ハンドラをインストールしています。

次に、MeasureItemイベントに対する、ハンドラを見てみましょう。

void miCat_MeasureItem(object sender, MeasureItemEventArgs e)
{
    e.ItemWidth = bmpCat.Width;
    e.ItemHeight = bmpCat.Height;
}
MeasureItemEventArgsオブジェクトの、ItemWidthとItemHeightプロパティを、bmpCatのそれに設定しています。

次に、実際の描画処理であるDrawItemイベントに対するハンドラを見てみましょう。

void miCat_DrawItem(object sender, DrawItemEventArgs e)
{
    Rectangle rc = e.Bounds;
    rc.X = (rc.Width - bmpCat.Width) / 2;
    rc.Width = bmpCat.Width;

    Graphics g = e.Graphics;

    e.DrawBackground();
    g.DrawImage(bmpCat, rc);
}
DrawItemEventArgsオブジェクトのBoundsプロパティで、描画される矩形領域をrcに取得しています。このrcはDrawImageメソッドで利用します。

イメージの描画がこの矩形いっぱいに表示されると、横長になってしまいます。

そこで、rc.Xとrc.Widthの値を調整します。

rc.Xは、表示されるメニュー項目の幅から、イメージの幅の差の半分としました。 そして、rc.Widthは、実際のイメージ幅としました。

これで、イメージはメニュー項目の中央に、伸縮なしで表示されるはずです。

e.DrawBackgroundメソッドで背景を塗ります。これを省略すると、選択時に背景の色が変わりません。

最期にDrawImageメソッドで、イメージを描画します。Graphics.DrawImageメソッドには無数のオーバーロードバージョンが存在します。ここで、使ったのは、次のバージョンです。

public void DrawImage (
	Image image,
	Rectangle rect
)
rectにimageが描画されます。

では、実行結果を見てみましょう。

猫が選択されていないときは、他のメニュー項目と同じで、猫の背景は白です。



猫が選択されていると、背景は青色になっています。




[C# フォーム Index] [C# コンソール Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 09/Nov/2006 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。