この章では、オーナードローという手法を用いてメニュー項目にイメージを表示してみます。
Win32APIによるプログラミングでは、オーナードローは結構面倒くさい部類に入っていました。しかし、C#では簡単です。
メニュー項目をオーナードローするには、
1.MenuItemオブジェクトのOwnerDrawプロパティをtrueに設定する。 2.MeasureItemイベントの処理 3.DrawItemイベントでの描画たったこれだけです。
MeasureItemイベントを処理するには、これにMeasureItemEventHandlerデリゲートを関連づけます。
MeasureItem += new MeasureItemEventHandler(miCat_MeasureItem);miCat_MeasureItem自作メソッドの第2引数にはMeasureItemEventArgsオブジェクトがきます。これにより、描画する範囲を指定できます。
MeasureItemEventArgsクラスは、まだやっていないリストボックス、コンボボックス、チェックボックスや、メニュー項目の各コントロールのMeasureItemイベントのデータを提供します。
このクラスのプロパティは4つあります。
| プロパティ | プロパティ値 | 意味 |
|---|---|---|
| Graphics | Graphics | 計測対象となるGraphicsオブジェクトの取得 |
| Index | int | 計測が必要な項目インデックスの取得 |
| ItemHeight | int | 項目の高さの取得・設定 |
| ItemWidth | int | 項目の幅の取得・設定 |
DrawItemイベントを処理するには、これにDrawItemEventHandlerデリゲートを関連づけます。
DrawItem += new DrawItemEventHandler(miCat_DrawItem);miCat_DrawItemの自作メソッドの第2引数は、DrawItemEventArgs型となります。
DrawItemEventArgsクラスは、DrawItemイベントのデータを提供します。
このクラスには7つのプロパティがあります。
| プロパティ | プロパティ値 | 意味 |
|---|---|---|
| BackColor | Color | 項目の背景色の取得 |
| Bounds | Rectangle | 描画項目の境界を表す四角形の取得 |
| Font | Font | 項目に割り当てられているフォントの取得 |
| ForeColor | Color | 項目の前景色の取得 |
| Graphics | Graphics | 描画するGraphicsの取得 |
| Index | int | 描画されている項目のインデックスの取得 |
| State | DrawItemState | 描画項目の状態の取得 |
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が描画されます。
では、実行結果を見てみましょう。
猫が選択されていないときは、他のメニュー項目と同じで、猫の背景は白です。
猫が選択されていると、背景は青色になっています。
Update 09/Nov/2006 By Y.Kumei