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