using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Resources;
using System.Windows.Forms;
// original menu extender code from: http://www.codeproject.com/cs/menu/menuimage.asp
namespace BetterMenu
{
// public delegate void SMEventDelegate(object sender, EventArgs e);
public class MainMenu : System.Windows.Forms.MainMenu, IXaml
{
private string name;
private object tag;
private static Hashtable menus=new Hashtable();
public string Name
{
get {return name;}
set
{
name=value;
if (menus.Contains(name))
{
menus[name]=this;
}
else
{
menus.Add(name, this);
}
}
}
public object Tag
{
get {return tag;}
set {tag=value;}
}
public event EventHandler ShowHelp;
public MainMenu()
{
ShowHelp=null;
}
public bool FireShowHelp(MenuItem mi)
{
bool ret=false;
if (ShowHelp != null)
{
ShowHelp(mi, EventArgs.Empty);
ret=true;
}
return ret;
}
protected void FireEvent(MenuItemCollection mis, string itemName)
{
// brute forcing it for now.
foreach (Menu m in mis)
{
if (m is MenuItem)
{
MenuItem mi=(MenuItem)m;
if (mi.Name==itemName)
{
mi.FireEvent();
return;
}
}
FireEvent(m.MenuItems, itemName);
}
}
static public void FireEvent(string menuName, string itemName)
{
if (menus.Contains(menuName))
{
MainMenu mm=(MainMenu)menus[menuName];
mm.FireEvent(mm.MenuItems, itemName);
}
}
}
public class MenuItem : System.Windows.Forms.MenuItem, IXaml
{
// private string onClick;
// private string data;
// private string helpText;
private Image image;
private Icon icon;
private static Image menuParentImage=null;
private string shortcutText;
private string helpText;
private string toolBarTag;
private string name;
private object tag;
public event EventHandler ShowHelp;
public Image Bitmap
{
get {return image;}
set
{
image=(Image)value;
}
}
public Icon Icon
{
get {return icon;}
set
{
icon=value;
MemoryStream mem=new MemoryStream();
icon.Save(mem);
image=new Bitmap(mem);
}
}
public string ShortcutText
{
get {return shortcutText;}
set {shortcutText=value;}
}
public string HelpText
{
get {return helpText;}
set {helpText=value;}
}
public string ToolBarTag
{
get {return toolBarTag;}
set {toolBarTag=value;}
}
public string Name
{
get {return name;}
set {name=value;}
}
public object Tag
{
get {return tag;}
set {tag=value;}
}
public Image MenuParentImage
{
get {return menuParentImage;}
}
public MenuItem() : base()
{
helpText=String.Empty;
ShowHelp=null;
OwnerDraw=true;
MeasureItem+=new MeasureItemEventHandler(OnMeasureItem);
DrawItem+=new DrawItemEventHandler(OnDrawItem);
Select+=new EventHandler(OnSelectEvent);
if (menuParentImage == null)
{
ResourceManager rm=ResourceManager.CreateFileBasedResourceManager("MenuIcons", @".\", null);
Icon icon=(Icon)rm.GetObject("MenuParent.ico");
MemoryStream mem=new MemoryStream();
icon.Save(mem);
menuParentImage=new Bitmap(mem);
}
}
public bool FireShowHelp(MenuItem mi)
{
bool ret=false;
if (ShowHelp != null)
{
ShowHelp(mi, EventArgs.Empty);
ret=true;
}
return ret;
}
public void FireEvent()
{
OnClick(EventArgs.Empty);
}
private void OnMeasureItem( Object sender, MeasureItemEventArgs e )
{
// retrieve the image list index from hash table
MenuItem menuItem = (MenuItem) sender ;
MenuHelper menuHelper = new MenuHelper( menuItem, e.Graphics, image);
// calculate the menu height
e.ItemHeight = menuHelper.CalcHeight() ;
e.ItemWidth = menuHelper.CalcWidth() ;
}
public void OnSelectEvent(object obj, System.EventArgs e)
{
// find a handler for the showing the help
Menu mi=(Menu)obj;
bool ret=false;
while ( (!ret) && (mi != null) )
{
if (mi is MainMenu)
{
ret=((MainMenu)mi).FireShowHelp((MenuItem)obj);
mi=null;
}
else if (mi is MenuItem)
{
ret=((MenuItem)mi).FireShowHelp((MenuItem)obj);
mi=((MenuItem)mi).Parent;
}
else if (mi is System.Windows.Forms.MenuItem)
{
mi=((System.Windows.Forms.MenuItem)mi).Parent;
}
}
}
/// <summary>
/// Event triggered to owner draw the provide <c>MenuItem</c>.
/// </summary>
/// <param name="sender">the menu item client object</param>
/// <param name="e">the event arguments</param>
private void OnDrawItem( Object sender, DrawItemEventArgs e )
{
// derive the MenuItem object, and create the MenuHelper
MenuItem menuItem = (MenuItem) sender ;
MenuHelper menuHelper = new MenuHelper( menuItem, e.Graphics, image);
// draw the menu background
bool menuSelected = (e.State & DrawItemState.Selected) > 0 ;
menuHelper.DrawBackground( e.Bounds, menuSelected ) ;
if ( menuHelper.IsSeperator() == true )
menuHelper.DrawSeperator( e.Bounds ) ;
else
{
menuHelper.DrawMenu( e.Bounds, menuSelected);
}
}
}
internal class MenuHelper
{
// some pre-defined buffer values for putting space between
// icon, menutext, seperator text, and submenu arrow indicators
private const int SEPERATOR_HEIGHT = 8 ;
private const int SBORDER_WIDTH = 3 ;
private const int BORDER_SIZE = SBORDER_WIDTH * 2 ;
private const int SBUFFER_WIDTH = 5 ;
private const int LBUFFER_WIDTH = 15 ;
private const int SHORTCUT_BUFFER_SIZE = 20 ;
private const int ARROW_WIDTH = 15 ;
private int IMAGE_WIDTH = SystemInformation.SmallIconSize.Width ;
private int IMAGE_HEIGHT = SystemInformation.SmallIconSize.Height ;
private int IMAGE_BUFFER_SIZE = SystemInformation.SmallIconSize.Width + 8 ;
// holds the local instances of the MenuItem and Graphics
// objects passed in through the Constructor
MenuItem _menuItem = null ;
Graphics _graphics = null ;
Image image=null;
/// <summary>
/// MenuHelper constructor to assist in owner drawn menus.
/// </summary>
/// <param name="menuItem">a <c>MenuItem</c> object to custom draw</param>
/// <param name="graphics">a <c>Graphics</c> object provided by the <c>MeasureItem</c> and <c>DrawItem</c> events</param>
public MenuHelper( MenuItem menuItem, Graphics graphics, Image image)
{
_menuItem = menuItem ;
_graphics = graphics ;
this.image = image;
}
/// <summary>
/// Based on the menu item text, and the <c>SystemInformation.SmallIconSize,</c>
/// performs a calculation to determine the correct <c>MenuItem</c> height.
/// </summary>
/// <returns>Returns an <c>int</c> value that contains the calculated height of the menu item.</returns>
public int CalcHeight()
{
// if the menu is a seperator, then return a fixed height
// otherwise calculate the menu size based on the system font
// and smalliconsize calculations (with some added buffer values)
if ( _menuItem.Text == "-" )
return SEPERATOR_HEIGHT ;
else
{
// depending on which is longer, set the menu height to either
// the icon, or the system menu font
if ( SystemInformation.MenuFont.Height > SystemInformation.SmallIconSize.Height )
return SystemInformation.MenuFont.Height + BORDER_SIZE ;
else
return SystemInformation.SmallIconSize.Height + BORDER_SIZE ;
}
}
/// <summary>
/// Based on the menu item text, and the <c>SystemInformation.SmallIconSize,</c>
/// performs a calculation to determine the correct <c>MenuItem</c> width.
/// </summary>
/// <returns>Returns an <c>int</c> value that contains the calculated width of the menu item.</returns>
public int CalcWidth()
{
// prepare string formatting used for rendering menu caption
StringFormat sf = new StringFormat() ;
sf.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show ;
// set the menu width by measuring the string, icon and buffer spaces
int menuWidth = (int) _graphics.MeasureString( _menuItem.Text, SystemInformation.MenuFont, 1000, sf).Width ;
int shortcutWidth = (int) _graphics.MeasureString( this.ShortcutText, SystemInformation.MenuFont, 1000, sf).Width ;
// if a top-level menu, no image support
if ( this.IsTopLevel() == true )
return menuWidth ;
else
return IMAGE_BUFFER_SIZE + menuWidth + SHORTCUT_BUFFER_SIZE + shortcutWidth ;
}
/// <summary>
/// A method to evaluate if the <c>MenuItem</c> has a shortcut selected, and the shortcut
/// has been selected for show.
/// </summary>
/// <returns>Returns True/False whether the menu has a shortcut to be displayed.</returns>
public bool HasShortcut()
{
return ( _menuItem.ShowShortcut == true && _menuItem.Shortcut != Shortcut.None ) ;
}
/// <summary>
/// Evaluates whether the <c>MenuItem</c> is a seperator by evaluating the text.
/// </summary>
/// <returns>Returns True/False whether the menu is a seperator.</returns>
public bool IsSeperator()
{
return ( _menuItem.Text == "-" ) ;
}
/// <summary>
/// Evaluates whether the <c>MenuItem</c> is a top-level menu that is sited directly
/// on a <c>MainMenu</c> control.
/// </summary>
/// <returns>Returns True/False if the menu item is a top-level menu.</returns>
public bool IsTopLevel()
{
return ( _menuItem.Parent is MainMenu ) ;
}
/// <summary>
/// Formats the <c>MenuItem</c> and returns the shortcut selection
/// as a displayable text string.
/// </summary>
/// <value>a string, the menu item shortcut as text</c></value>
public string ShortcutText
{
get
{
if ( _menuItem.ShowShortcut == true && _menuItem.Shortcut != Shortcut.None )
{
if (((MenuItem)_menuItem).ShortcutText != "")
{
return ((MenuItem)_menuItem).ShortcutText;
}
// Keys keys=(Keys)_menuItem.Shortcut;
// string key=System.ComponentModel.TypeDescriptor.GetConverter(keys.GetType()).ConvertToString(keys) ;
// return "\t"+key;
// return _menuItem.Shortcut.ToString();
}
return null ;
}
}
/// <summary>
/// Draws a normal menu item including any related icons, checkboxes,
/// menu text, shortcuts text, and parent/submenu arrows.
/// </summary>
/// <param name="bounds">a <c>Rectangle</c> that holds the drawing canvas boundaries</param>
/// <param name="selected">True/False if the menu item is currently selected</param>
public void DrawMenu ( Rectangle bounds, bool selected)
{
// draw the menu text
DrawMenuText( bounds, selected ) ;
// since icons make the menu height longer,
// paint a custom arrow if the menu is a parent
// to augment the one painted by the control
// HACK: The default arrow shows up even for ownerdrawn controls ???
if ( _menuItem.IsParent == true )
{
this.DrawArrow(((MenuItem)_menuItem).MenuParentImage, bounds) ;
}
// if the menu item is checked, ignore any menuimage index
// and draw the checkbox, otherwise draw the custom image
if ( _menuItem.Checked )
DrawCheckBox ( bounds ) ;
else
{
// see if the menu item has an icon associated and draw image
if (image != null)
{
DrawImage(image, bounds) ;
}
}
}
/// <summary>
/// Draws the <c>MenuItem</c> background.
/// </summary>
/// <param name="bounds">a <c>Rectangle</c> that holds the painting canvas boundaries</param>
/// <param name="selected">True/False if the menu item is currently selected</param>
public void DrawBackground( Rectangle bounds, bool selected )
{
// if selected then paint the menu as highlighted,
// otherwise use the default menu brush
if (selected == true)
{
// _graphics.FillRectangle(SystemBrushes.Highlight, bounds);
_graphics.FillRectangle(new SolidBrush(Color.FromArgb(180, 180, 255)), bounds);
_graphics.DrawRectangle(new Pen(Color.Blue), bounds.X, bounds.Y, bounds.Width-1, bounds.Height-1);
// _graphics.FillRectangle(SystemBrushes.InactiveCaption, bounds);
// _graphics.DrawRectangle(new Pen(SystemBrushes.ActiveCaption), bounds.X, bounds.Y, bounds.Width-1, bounds.Height-1);
}
else
{
Rectangle imageMargin=bounds;
imageMargin.Width=IMAGE_BUFFER_SIZE-2;
bounds.X+=imageMargin.Width;
_graphics.FillRectangle(SystemBrushes.ControlLight, imageMargin);
_graphics.FillRectangle( SystemBrushes.Menu, bounds ) ;
}
}
/// <summary>
/// Draws a menu seperator.
/// </summary>
/// <param name="bounds">a <c>Rectangle</c> that holds the drawing canvas boundaries</param>
public void DrawSeperator( Rectangle bounds )
{
// create the seperator line pen
Pen pen = new Pen(SystemColors.ControlDark) ;
// calculate seperator boundaries
int xLeft = bounds.Left + IMAGE_BUFFER_SIZE ;
int xRight = xLeft + bounds.Width ;
int yCenter = bounds.Top + (bounds.Height / 2) ;
// draw a seperator line and return
_graphics.DrawLine(pen, xLeft, yCenter, xRight, yCenter) ;
}
/// <summary>
/// Draws the text for an ownerdrawn <c>MenuItem</c>.
/// </summary>
/// <param name="bounds">a <c>Rectangle</c> that holds the drawing area boundaries</param>
/// <param name="selected">True/False whether the menu item is currently selected</param>
private void DrawMenuText ( Rectangle bounds, bool selected )
{
// use system fonts and colors to select the menu brush so the menu
// will appear correctly for any desktop theme
Font menuFont = SystemInformation.MenuFont ;
SolidBrush menuBrush = null ;
if ( _menuItem.Enabled == false )
menuBrush = new SolidBrush( SystemColors.GrayText ) ;
else
{
// if ( selected == true )
// menuBrush = new SolidBrush( SystemColors.HighlightText ) ;
// else
menuBrush = new SolidBrush( SystemColors.MenuText ) ;
}
int width=bounds.Left+IMAGE_BUFFER_SIZE;
// draw the menu text
StringFormat sfMenu = new StringFormat() ;
sfMenu.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show ;
_graphics.DrawString( _menuItem.Text, menuFont, menuBrush, width, bounds.Top + ((bounds.Height - menuFont.Height) / 2), sfMenu ) ;
// if the menu has a shortcut, then also
// draw the shortcut right aligned
if ( this.IsTopLevel() != true || this.HasShortcut() == false )
{
StringFormat sfShortcut = new StringFormat() ;
sfShortcut.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show ;
sfShortcut.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
int shortcutWidth = (int) _graphics.MeasureString( this.ShortcutText, menuFont, 1000, sfShortcut).Width ;
_graphics.DrawString(this.ShortcutText, menuFont, menuBrush, (bounds.Width) - LBUFFER_WIDTH , bounds.Top + ((bounds.Height - menuFont.Height) / 2), sfShortcut);
}
}
/// <summary>
/// Draws a checked item next to a <c>MenuItem</c>.
/// </summary>
/// <param name="bounds">a <c>Rectangle</c> that identifies the drawing area boundaries</param>
private void DrawCheckBox( Rectangle bounds )
{
// use the very handy ControlPaint object to paint
// a checkbox. ButtonState is a bitwise flat that can be built
// to accomodate style and state appearance
ButtonState btnState = ButtonState.Flat ;
if ( _menuItem.Checked == true )
btnState = btnState | ButtonState.Checked ;
if ( _menuItem.Enabled == false )
btnState = btnState | ButtonState.Inactive ;
// draw the checkbox
ControlPaint.DrawCheckBox(_graphics, bounds.Left + SBORDER_WIDTH, bounds.Top + ((bounds.Height - IMAGE_HEIGHT) / 2), IMAGE_WIDTH, IMAGE_HEIGHT, btnState ) ;
}
/// <summary>
/// Draws a provided image onto the <c>MenuItem</c>.
/// </summary>
/// <param name="menuImage">an <c>Image</c> to paint on the menu</param>
/// <param name="bounds">a <c>Rectangle</c> that holds the drawing space boundaries</param>
private void DrawImage( Image menuImage, Rectangle bounds )
{
// if the menu item is enabled, then draw the image normally
// otherwise draw it as disabled
if ( _menuItem.Enabled == true )
_graphics.DrawImage(menuImage, bounds.Left + SBORDER_WIDTH, bounds.Top + ((bounds.Height - IMAGE_HEIGHT) / 2), IMAGE_WIDTH, IMAGE_HEIGHT ) ;
else
ControlPaint.DrawImageDisabled(_graphics, menuImage, bounds.Left + SBORDER_WIDTH, bounds.Top + ((bounds.Height - IMAGE_HEIGHT) / 2), SystemColors.Menu ) ;
}
/// <summary>
/// Draws a custom arrow on the right-side edge of the menu to indicate
/// the menu has submenu items. Used to supplement a base contorl arrow
/// that is painted incorrectly (seems to be a bug), and make the arrow
/// appear correctly for longer menu items.
/// </summary>
/// <param name="menuImage"></param>
/// <param name="bounds"></param>
private void DrawArrow( Image menuImage, Rectangle bounds )
{
_graphics.DrawImage(menuImage, bounds.Left + bounds.Width - ARROW_WIDTH, bounds.Top + ((bounds.Height - IMAGE_HEIGHT) / 2), IMAGE_WIDTH, IMAGE_HEIGHT ) ;
}
}
}