メインメニューのオーナードロー
開発環境: Visual Studio 2003
メニューの表示で左側にアイコンが表示されていますが、標準の ManuItem
コンポーネントではサポートされていません。このぐらい、サポートしておいてくれよという感じですね。Windows MFC/Win32 でも OwnerDraw という機構がサポートされていたようですが(詳しく知らない)、.NET/C# でも オーナー描画がサポートされています。
次の図は、メニュー項目のオーナー描画の例です。
描画部分は通常のグラフィクス描画ができます。この例では、アイコンとフォントの変更だけですが、バックグラウンドの変更なども可能なので、かっこいいメニューがつくれるはずです。
上の図のデザインがかっちょ悪いというコメントは、却下 ^ ^/。
(1) VisualStudio.NET
のサンプルコード
オーナー描画の作り方は次のとおりで、それほど難しい話ではありませんでした。オーナー描画のサンプルコードは、VS.NET のサンプルに載っているので、そちらを参照してもらえればよいでしょう。以下では、簡単な説明だけ。
作り方説明
1、プロジェクトの作成。
Windows.Forms アプリケーションを作ります。
2、メインメニューの作成。
ツールバーより、MainMenu をドラッグアンドドロップします。
3、メニュー項目の追加
フォームエディタで、メニュー項目を追加します。
4、OwnerDraw を有効にする。
メニュー項目の
OwnerDraw プロパティを true に設定し、オーナー描画を有効にします。
5、MeasureItem イベントハンドラを追加する。
MeasureItem イベントハンドラを登録します。これにより、メニューにメニュー項目を描画する前に、MeasureItem イベントが発生します。このイベントハンドラの中で、オーナードローしようとするサイズを ItemHeight, ItemWidth で指定することができます。
注意: ListBox のオーナー描画では、ListBox の
DrawModeプロパティをOwnerDrawVariable にしないと、このMeasureItem
イベントは発生しませんが、メニュー項目では、このDrawMode プロパティは存在しません。
private void MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
{
// オーナー描画しようとしているメニュー項目のサイズを返す。
e.ItemHeight = ......
e.ItemWidth = ......
}
6、DrawItem イベントハンドラを追加する。
DrawItem イベントハンドラで、メニュー項目そのものを自分で描画します。これは、メニュー項目を実際に描画するときに、システム側から呼ばれます。この中で、アイコンや、テキストなどを自分の好みで描画します。イベントデータとして、BackColor, Bounds, Font, ForeColor, Graphics, Index,
State が渡されます。
private void DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
// 実際にメニュー項目をオーナー描画する。
// 描画に必要な情報は、DrawItemEventArgs e で与えられる。
// 例: 描画領域は、e.Bounds、フォントは e.Font、
// グラフィクスコンテクストは、e.Graphicsなど。
}
4で説明した方法で組もうとすると、Formsのイベントハンドラの DrawItem
の中でごちゃごちゃ書かないといけないので、ちょっと見苦しくなります。そこで、MenuItem を継承して、オーナードローするクラスを作ってみました。
ほかにいい方法があるのかわかりませんが、少なくともメニュー項目と、アプリケーション本体の分離はできました。 もうひとつのメリットとしては フォームデザイナーから、アイコン、フォントが修正できるので、かなりラクチンです。
注意: フォームデザイナーが完全にデライブドクラスをサポートしてくれるわけではありません。このため、”デザイナ サポートに必要なメソッドです。このメソッドの内容をコード エディタで変更しないでください。” というエリアのコードを修正しないといけないので、修正には注意が必要です。
完全にMainMenu, MenuItem をオーバーライドできればいいのですが。。。。
もっとうまい方法など、コメントお待ちしてます。
簡単な説明
OwnerDrawMenuItem.cs
MenuItem から継承。
プロパティで、アイコン4種を追加。
−標準の状態のアイコン、
−チェックされている状態のアイコン
−ラジオチェックされている状態のアイコン
−ディスエーブル状態のアイコン
テキストの色をしているためのプロパティを追加。
テキストのフォントを指定するためのプロパティを追加。
MeasureItem がコールされたときサイズを返すための GetHeight(...) メソッドを追加。
DrawItem がコールされたときに呼び出す OwnerDrawItem(...) メソッドを追加。
Form1.cs
メインアプリケーション。
OwnerDrawMenuItem.cs の使い方
1、プロジェクトの作成。
Windows.Forms アプリケーション(Form1.cs)を作ります。
2、メインメニューの作成。
ツールバーより、MainMenu をドラッグアンドドロップします。
3、メニュー項目の追加
フォームエディタで、メニュー項目を追加します。
4、OwnerDrawMenuItem.csの追加
OwnerDrawMenuItem.cs を追加します。
名前空間
using Uchukamen.Forms;
が必要になります。
5、MenuItem インスタンス定義の変更
たとえば、menuItem4 をオーナードローにする場合は、
private System.Windows.Forms.MenuItem menuItem4;
を継承クラス
private Uchukamen.Forms.OwnerDrawMenuItem
menuItem4;
に変更する。
6、MenuItem インスタンス生成部分の変更
たとえば、menuItem4 をオーナードローにする場合は、
this.menuItem4 = new System.Windows.Forms.MenuItem();
を 継承クラス
this.menuItem4 = new
Uchukamen.Forms.OwnerDrawMenuItem();
に変更する。
7、MenuItem のプロパティを変更
ここまでくれば、下図のようにフォームデザイナーが使えます。
次のようにフォームデザイナーで、menuItem のプロパティを開き、
アイコン、オーナードローフォント、オーナードローフォントカラー、テキストなどを指定します。
注意: OwnerDraw プロパティを True
にすることを忘れると、オーナードローしません。
つい忘れがち。
8、MenuItem のイベントハンドラを追加
同様に、フォームデザイナーで、menuItem のイベントを開き、
DrawItem, MeasureItem のイベントハンドラを追加します。
9、イベントハンドラのコーディング
DrawItem では、単にOwnerDrawItem を呼び出すだけです。
menuItems_DrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
((OwnerDrawMenuItem)sender).OwnerDrawItem(e);
}
MeasureItem では、OwnerDrawMenuItem のGetHeight メソッド呼び出しの結果を ItemHeight,
ItemWidth にセットするだけです。
menuItems_MeasureItem(object sender,
System.Windows.Forms.MeasureItemEventArgs e)
{
Size size =
((OwnerDrawMenuItem)sender).GetHeight(e);
e.ItemHeight = size.Height;
e.ItemWidth = size.Width;
}
注意: MenuItem では、メニュー項目ごとにこれらが呼ばれるので、MenuItem
のインスタンスすべてに共通のイベントハンドラでOKです。
このため、Form1.cs 側で手でコーディングする必要があるのは、この表の赤字で書いた部分だけです。
Ver. 1.0 2002/9/1
メインアプリ Form1.cs
MenuItem を継承したクラス OwnerDrawMenuItem.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Drawing.Drawing2D;
using Uchukamen.Forms;
namespace OwnerDrawMenu1
{
/// <summary>
/// Form1 の概要の説明です。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem2;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem3;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem4;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem5;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem6;
private Uchukamen.Forms.OwnerDrawMenuItem menuItem7;
/// <summary>
/// 必要なデザイナ変数です。
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Windows フォーム デザイナ サポートに必要です。
//
InitializeComponent();
//
// TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。
//
}
/// <summary>
/// 使用されているリソースに後処理を実行します。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.menuItem2 = new Uchukamen.Forms.OwnerDrawMenuItem();
this.menuItem3 = new Uchukamen.Forms.OwnerDrawMenuItem();
this.menuItem4 = new Uchukamen.Forms.OwnerDrawMenuItem();
this.menuItem5 = new Uchukamen.Forms.OwnerDrawMenuItem();
this.menuItem6 = new Uchukamen.Forms.OwnerDrawMenuItem();
this.menuItem7 = new Uchukamen.Forms.OwnerDrawMenuItem();
//
// mainMenu1
//
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1});
//
// menuItem1
//
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem2,
this.menuItem3,
this.menuItem4,
this.menuItem5,
this.menuItem6,
this.menuItem7});
this.menuItem1.Text = "C#プログラミング";
//
// menuItem2
//
this.menuItem2.CheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem2.CheckIcon")));
this.menuItem2.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem2.DefaultIcon")));
this.menuItem2.DisabledIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem2.DisabledIcon")));
this.menuItem2.Index = 0;
this.menuItem2.OwnerDraw = true;
this.menuItem2.OwnerDrawFont = null;
this.menuItem2.OwnerDrawFontColor = System.Drawing.Color.Empty;
this.menuItem2.RadioCheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem2.RadioCheckIcon")));
this.menuItem2.Text = "通常のアイコン";
this.menuItem2.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem2.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// menuItem3
//
this.menuItem3.Checked = true;
this.menuItem3.CheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem3.CheckIcon")));
this.menuItem3.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem3.DefaultIcon")));
this.menuItem3.DisabledIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem3.DisabledIcon")));
this.menuItem3.Index = 1;
this.menuItem3.OwnerDraw = true;
this.menuItem3.OwnerDrawFont = new System.Drawing.Font("MS UI Gothic", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.menuItem3.OwnerDrawFontColor = System.Drawing.Color.Empty;
this.menuItem3.RadioCheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem3.RadioCheckIcon")));
this.menuItem3.Text = "チェックされたアイコン";
this.menuItem3.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem3.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// menuItem4
//
this.menuItem4.Checked = true;
this.menuItem4.CheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem4.CheckIcon")));
this.menuItem4.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem4.DefaultIcon")));
this.menuItem4.DisabledIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem4.DisabledIcon")));
this.menuItem4.Index = 2;
this.menuItem4.OwnerDraw = true;
this.menuItem4.OwnerDrawFont = new System.Drawing.Font("MS UI Gothic", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.menuItem4.OwnerDrawFontColor = System.Drawing.Color.Magenta;
this.menuItem4.RadioCheck = true;
this.menuItem4.RadioCheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem4.RadioCheckIcon")));
this.menuItem4.Text = "ラジオチェック";
this.menuItem4.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem4.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// menuItem5
//
this.menuItem5.CheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem5.CheckIcon")));
this.menuItem5.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem5.DefaultIcon")));
this.menuItem5.DisabledIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem5.DisabledIcon")));
this.menuItem5.Enabled = false;
this.menuItem5.Index = 3;
this.menuItem5.OwnerDraw = true;
this.menuItem5.OwnerDrawFont = new System.Drawing.Font("MS UI Gothic", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.menuItem5.OwnerDrawFontColor = System.Drawing.Color.Empty;
this.menuItem5.RadioCheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem5.RadioCheckIcon")));
this.menuItem5.Text = "無効状態";
this.menuItem5.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem5.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// menuItem6
//
this.menuItem6.CheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem6.CheckIcon")));
this.menuItem6.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem6.DefaultIcon")));
this.menuItem6.DisabledIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem6.DisabledIcon")));
this.menuItem6.Index = 4;
this.menuItem6.OwnerDraw = true;
this.menuItem6.OwnerDrawFont = new System.Drawing.Font("MS UI Gothic", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.menuItem6.OwnerDrawFontColor = System.Drawing.Color.Empty;
this.menuItem6.RadioCheckIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem6.RadioCheckIcon")));
this.menuItem6.Text = "小さなアイコン";
this.menuItem6.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem6.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// menuItem7
//
this.menuItem7.CheckIcon = null;
this.menuItem7.DefaultIcon = ((System.Drawing.Icon)(resources.GetObject("menuItem7.DefaultIcon")));
this.menuItem7.DisabledIcon = null;
this.menuItem7.Index = 5;
this.menuItem7.OwnerDraw = true;
this.menuItem7.OwnerDrawFont = new System.Drawing.Font("MS UI Gothic", 36F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.menuItem7.OwnerDrawFontColor = System.Drawing.Color.Empty;
this.menuItem7.RadioCheckIcon = null;
this.menuItem7.Text = "大きなアイコン";
this.menuItem7.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.menuItems_DrawItem);
this.menuItem7.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(this.menuItems_MeasureItem);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(7, 16);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Font = new System.Drawing.Font("MS UI Gothic", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(128)));
this.Menu = this.mainMenu1;
this.Name = "Form1";
this.Text = "Form1";
}
#endregion
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void menuItems_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
((OwnerDrawMenuItem)sender).OwnerDrawItem(e);
}
private void menuItems_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
{
Size size = ((OwnerDrawMenuItem)sender).GetHeight(e);
e.ItemHeight = size.Height;
e.ItemWidth = size.Width;
}
}
}
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
namespace Uchukamen.Forms
{
public class OwnerDrawMenuItem: MenuItem
{
#region プロパティ
#region オーナードローフォントプロパティ
private Font ownerDrawFont;
public Font OwnerDrawFont
{
get
{
return ownerDrawFont;
}
set
{
ownerDrawFont = value;
}
}
#endregion
#region オーナードローフォントカラープロパティ
private Color ownerDrawFontColor;
public Color OwnerDrawFontColor
{
get
{
return ownerDrawFontColor;
}
set
{
ownerDrawFontColor = value;
}
}
#endregion
#region 無効アイコンプロパティ
private Icon disabledIcon;
public Icon DisabledIcon
{
get
{
return disabledIcon;
}
set
{
disabledIcon = value;
}
}
#endregion
#region アイコンプロパティ
private Icon defaultIcon;
public Icon DefaultIcon
{
get
{
return defaultIcon;
}
set
{
defaultIcon = value;
}
}
#endregion
#region チェックアイコンプロパティ
private Icon checkIcon;
public Icon CheckIcon
{
get
{
return checkIcon;
}
set
{
checkIcon = value;
}
}
#endregion
#region ラジオチェックアイコンプロパティ
private Icon radioCheckIcon;
public Icon RadioCheckIcon
{
get
{
return radioCheckIcon;
}
set
{
radioCheckIcon = value;
}
}
#endregion
#endregion
public Size GetHeight(System.Windows.Forms.MeasureItemEventArgs e)
{
#region アイコンのサイズを求める。
Icon icon = null;
Size iSize = new Size(0,0);
if ( Enabled == false && (disabledIcon != null))
icon = disabledIcon;
else if ( Enabled && Checked && RadioCheck && (radioCheckIcon != null))
icon = radioCheckIcon;
else if ( Enabled && Checked && (RadioCheck == false) && (checkIcon != null))
icon = checkIcon;
else if ( Enabled && defaultIcon != null )
icon = defaultIcon;
iSize = icon.Size;
#endregion
#region メニューの文字列のサイズを求める。
Size stringSize = new Size(0,0);
if (this.ownerDrawFont != null)
stringSize = e.Graphics.MeasureString(this.Text, ownerDrawFont).ToSize();
#endregion
return new Size(icon.Width + stringSize.Width, Math.Max(icon.Height, stringSize.Height));
}
public void OwnerDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
// 一般的に、DrawBackground() は drawing の前に呼ばれるべきです。
// 領域を完全にオーナードローする場合には、余分です。
e.DrawBackground();
// e.graphics オーナードローで使用するグラフィクスコンテキスト。
Graphics g = e.Graphics;
#region アイコンを描画する。
Size iconSize = new Size(0,0);
if ( Enabled == false && (disabledIcon != null))
{
// 無効アイコンの描画
// チェックアイコンの描画
Rectangle disabledIconBounds = e.Bounds;
iconSize = disabledIconBounds.Size = disabledIcon.Size;
g.DrawIcon(disabledIcon, disabledIconBounds);
}
else if ( Checked && RadioCheck && (radioCheckIcon != null))
{
// ラジオチェックアイコンの描画
// チェックアイコンの描画
Rectangle radioCheckIconBounds = e.Bounds;
iconSize = radioCheckIconBounds.Size = radioCheckIcon.Size;
g.DrawIcon(radioCheckIcon, radioCheckIconBounds);
}
else if ( Checked && (RadioCheck == false) && (checkIcon != null))
{
// チェックアイコンの描画
Rectangle checkIconBounds = e.Bounds;
iconSize = checkIconBounds.Size = checkIcon.Size;
g.DrawIcon(checkIcon, checkIconBounds);
}
else if ( defaultIcon != null )
{
// 通常のアイコンの描画
Rectangle iconBounds = e.Bounds;
iconSize = iconBounds.Size = defaultIcon.Size;
g.DrawIcon(defaultIcon, iconBounds);
}
#endregion
#region テキストの描画
// 現在のフォントの高さを求める。
Font currentFont = null;
if ( OwnerDrawFont != null )
currentFont = OwnerDrawFont;
else
currentFont = e.Font;
Size stringSize=g.MeasureString(this.Text, currentFont).ToSize();
Rectangle textBounds = new Rectangle(e.Bounds.X + iconSize.Width, e.Bounds.Y,
e.Bounds.Width, Math.Max( defaultIcon.Height, stringSize.Height));
// 文字列を書く。
Brush tBrush;
if ( Enabled )
if ( OwnerDrawFontColor.IsEmpty )
tBrush = new SolidBrush( Color.FromKnownColor(KnownColor.MenuText) );
else
tBrush = new SolidBrush( OwnerDrawFontColor );
else
tBrush = new SolidBrush( Color.FromKnownColor(KnownColor.GrayText) );
g.DrawString( Text, currentFont, tBrush, textBounds );
#endregion
// フォーカスをあらわす四角を描く。
e.DrawFocusRectangle();
}
}
}