using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
//================================================================
//
// Created: 3/17/07
//
// Namespace: MenuTest
//
// Component: XMenuItemExtender
//
// Author: Mike Hankey
//
// Special Thanks: The IExtenderProvider concept was provided by
// a CodeProject article:
// "Getting to know IExtenderProvider" by James T. Johnson
//
// Definition:
//
//================================================================
namespace MenuTest
{
#region Extender stuff
/// <summary>
/// Properties provided by extender
/// </summary>
[ProvideProperty("XPMenuItem", typeof(MenuItem))]
[ProvideProperty("XPMenuItemImage", typeof(MenuItem))]
[ProvideProperty("XPHeaderText", typeof(MenuItem))]
[ProvideProperty("XPHeaderImage", typeof(MenuItem))]
class XMenuItemExtender : Component, IExtenderProvider
{
//Required Hash Table to cache MenuItem components
private Hashtable properties;
//The checkBox image displayed when item checked
private Image _checkImage = null;
public XMenuItemExtender()
{
properties = new Hashtable();
_checkImage = global::MenuTest.Properties.Resources.OK_CheckBox;
}
public XMenuItemExtender(IContainer parent) : this()
{
parent.Add(this);
_checkImage = global::MenuTest.Properties.Resources.OK_CheckBox;
}
bool IExtenderProvider.CanExtend(object o)
{
if (o is MenuItem)
return true;
return false;
}
private Properties EnsurePropertyExists(object key)
{
Properties p = (Properties) properties[key];
if (p == null)
{
p = new Properties();
//After creating the property we assign a reference to the
// MenuItem associated with it. If we didn't do this we would
// have to manually assign the item to the extender in the designer.
p._menuItem = (MenuItem) key;
properties[key] = p;
}
return p;
}
/// <summary>
/// Class containing provider fields
/// </summary>
internal class Properties
{
public MenuItem _menuItem = null;
public Image _menuImage = null;
public string _headerText = string.Empty;
public Image _headerImage = null;
public Properties()
{
}
}
/// <summary>
/// XPMenuItem Property
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
public MenuItem GetXPMenuItem(MenuItem mi)
{
return EnsurePropertyExists(mi)._menuItem;
}
public void SetXPMenuItem(MenuItem mis, MenuItem mid)
{
EnsurePropertyExists(mis)._menuItem = mid;
//Add or remove event handlers for the required events
if (mid != null)
{
mis.MeasureItem += new MeasureItemEventHandler(OnMeasureItem);
mis.DrawItem += new DrawItemEventHandler(OnDrawItem);
mis.Click += new EventHandler(OnClick);
}
else
{
mis.MeasureItem -= new MeasureItemEventHandler(OnMeasureItem);
mis.DrawItem -= new DrawItemEventHandler(OnDrawItem);
mis.Click -= new EventHandler(OnClick);
}
}
/// <summary>
/// XPMenuItemImage Property
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
public Image GetXPMenuItemImage(MenuItem mi)
{
return EnsurePropertyExists(mi)._menuImage;
}
public void SetXPMenuItemImage(MenuItem mi, Image img)
{
EnsurePropertyExists(mi)._menuImage = img;
}
/// <summary>
/// XPHeaderText Property
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
public string GetXPHeaderText(MenuItem mi)
{
return EnsurePropertyExists(mi)._headerText;
}
public void SetXPHeaderText(MenuItem mi, string str)
{
EnsurePropertyExists(mi)._headerText = str;
}
/// <summary>
/// XPHeaderImage Property
/// </summary>
/// <param name="mi"></param>
/// <returns></returns>
public Image GetXPHeaderImage(MenuItem mi)
{
return EnsurePropertyExists(mi)._headerImage;
}
public void SetXPHeaderImage(MenuItem mi, Image img)
{
EnsurePropertyExists(mi)._headerImage = img;
}
#endregion
#region Owner Drawn Event Handlers
/// <summary>
/// Handles any custom Mouse click handling for the item.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnClick(object sender, EventArgs e)
{
MenuItem mi = (MenuItem)sender;
if (mi.Checked)
mi.Checked = false;
}
private void OnMeasureItem(object sender, MeasureItemEventArgs e)
{
MenuItem mi = (MenuItem)sender;
if (mi.Text == "-")
e.ItemHeight = 3;
else
{
if (mi.Parent == mi.Parent.GetMainMenu())
{
e.ItemHeight = 16;
e.ItemWidth = 50;
}
else
{
e.ItemHeight = 20;
e.ItemWidth = 100;
}
}
}
private void OnDrawItem(object sender, DrawItemEventArgs e)
{
MenuItem mi = (MenuItem)sender;
Rectangle rct = new Rectangle(e.Bounds.X + 20, e.Bounds.Y, e.Bounds.Width - 20, e.Bounds.Height);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
if (mi.Parent == mi.Parent.GetMainMenu())
DrawRootMenuItem(e, mi);
else
{
if (e.Index == 0)
DrawMenuHeader(e, mi);
DrawMenuImage(e, mi);
if (mi.Text == "-")
DrawSeparator(e);
else
DrawMenuItem(e, rct, mi);
}
}
#endregion
#region Helper methods
private void DrawSeparator(DrawItemEventArgs e)
{
Color clr2a = Color.FromArgb(172, 175, 137);
Rectangle rct = new Rectangle(
new Point(e.Bounds.X + 42, e.Bounds.Y),
new Size(e.Bounds.Width - 44, e.Bounds.Height));
SolidBrush br = new SolidBrush(clr2a);
e.Graphics.FillRectangle(br, rct);
br.Dispose();
}
private void DrawRootMenuItem(DrawItemEventArgs e, MenuItem mi)
{
GraphicsPath gp = null;
Rectangle rct = e.Bounds;
Color clr1 = Color.White;
Color clr2 = Color.FromArgb(230, 225, 204);
Rectangle txtRct = new Rectangle(rct.X + 20,
rct.Y,
rct.Width - 20,
rct.Height);
LinearGradientBrush txtLgb = new LinearGradientBrush(txtRct, clr1, clr2, 90f);
Color clr1a = Color.FromArgb(120, Color.White);
Color clr2a = Color.FromArgb(140, 172, 175, 137);
Rectangle selRct = new Rectangle(rct.X + 2,
rct.Y + 2,
rct.Width - 4,
rct.Height - 4);
LinearGradientBrush selLgb = new LinearGradientBrush(selRct, clr1a, clr2a, 90f);
gp = DrawBorder(e, rct, BorderTypes.rounded);
DrawItemState state = e.State & DrawItemState.Selected;
if (state == DrawItemState.Selected)
e.Graphics.FillRectangle(selLgb, rct);
else
e.Graphics.FillPath(txtLgb, gp);
Image img = ((Properties) properties[mi])._menuImage;
string str = mi.Text;
Font fnt = new Font("Arial", 8, FontStyle.Bold | FontStyle.Italic);
SolidBrush br = new SolidBrush(Color.DimGray);
SizeF sz = e.Graphics.MeasureString(str, fnt);
if (img != null)
{
rct.Size = new Size(16, 16);
rct.Offset(2, 2);
e.Graphics.DrawImage(img, rct);
}
txtRct.Y = e.Bounds.Y + (int)(txtRct.Height - sz.Height) / 2;
e.Graphics.DrawString(str, fnt, br, txtRct);
txtLgb.Dispose();
selLgb.Dispose();
fnt.Dispose();
br.Dispose();
}
private void DrawMenuImage(DrawItemEventArgs e, MenuItem mi)
{
Rectangle rct = new Rectangle(e.Bounds.X + 20,
e.Bounds.Y, 20, e.Bounds.Height + 1);
SolidBrush br = new SolidBrush(Color.FromArgb(230, 225, 204));
e.Graphics.FillRectangle(br, rct);
Image img = null;
if (mi.Checked)
img = _checkImage;
else
img = ((Properties)properties[mi])._menuImage;
if (img != null)
e.Graphics.DrawImage(img, new Rectangle(rct.X + 2, rct.Y + 2, 16, 16));
br.Dispose();
}
private void DrawMenuItem(DrawItemEventArgs e, Rectangle rct, MenuItem mi)
{
Color clr1 = Color.White;
Color clr2 = Color.FromArgb(230, 225, 204);
Rectangle txtRct = new Rectangle(rct.X + 20,
rct.Y,
rct.Width - 20,
rct.Height);
LinearGradientBrush txtLgb = new LinearGradientBrush(txtRct, clr1, clr2, 90f);
Color clr1a = Color.FromArgb(140, Color.White);
Color clr2a = Color.FromArgb(140, 172, 175, 137);
Rectangle selRct = new Rectangle(rct.X + 2,
rct.Y + 2,
rct.Width - 4,
rct.Height - 4);
LinearGradientBrush selLgb = new LinearGradientBrush(selRct, clr1a, clr2a, 90f);
DrawItemState state = e.State & DrawItemState.Selected;
SolidBrush br = null;
Font fnt = null;
if (state == DrawItemState.Selected)
{
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), selRct);
e.Graphics.FillRectangle(selLgb, selRct);
fnt = new Font("Arial", 8, FontStyle.Bold | FontStyle.Italic);
br = new SolidBrush(Color.Black);
}
else
{
e.Graphics.FillRectangle(txtLgb, txtRct);
fnt = new Font("Arial", 8, FontStyle.Bold | FontStyle.Italic);
br = new SolidBrush(Color.SlateGray);
}
string str = mi.Text;
SizeF sz = e.Graphics.MeasureString(str, fnt);
txtRct.X += 2;
txtRct.Y += (int)(rct.Height - sz.Height) / 2;
e.Graphics.DrawString(str, fnt, br, txtRct);
fnt.Dispose();
br.Dispose();
selLgb.Dispose();
txtLgb.Dispose();
}
private void DrawMenuHeader(DrawItemEventArgs e, MenuItem mi)
{
Rectangle hrct = new Rectangle(0, 0, 20, mi.Parent.MenuItems.Count * 20);
Color clr1 = Color.White;
Color clr3 = Color.FromArgb(194, 196, 176);
LinearGradientBrush hlgb = new LinearGradientBrush(hrct, clr1, clr3, 0f);
GraphicsPath gp = null;
gp = DrawBorder(e, hrct, BorderTypes.none);
e.Graphics.FillPath(hlgb, gp);
if (((Properties)properties[mi])._headerImage != null)
{
hrct.Size = new Size(16, 16);
Image img = ((Properties)properties[mi])._headerImage;
e.Graphics.DrawImage(img, hrct);
}
StringFormat strFmt = new StringFormat();
strFmt.FormatFlags = StringFormatFlags.DirectionVertical;
Font fnt = new Font("Arial", 8, FontStyle.Bold | FontStyle.Italic);
SolidBrush br = new SolidBrush(Color.DarkSlateGray);
string str = ((Properties)properties[mi])._headerText;
hrct.Y += 16;
hrct.Height -= 20;
e.Graphics.DrawString(str, fnt, br, hrct.Location, strFmt);
br.Dispose();
fnt.Dispose();
gp.Dispose();
hlgb.Dispose();
}
private const int ArcWidth = 12;
public enum BorderTypes
{
none,
square,
rounded
};
/// <summary>
/// Draw the Border around the item
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
protected GraphicsPath DrawBorder(DrawItemEventArgs e, Rectangle rct, BorderTypes _borderType)
{
GraphicsPath gp = new GraphicsPath();
//rct.Offset(1, 1);
rct.Width -= 1;
rct.Height -= 1;
if (_borderType == BorderTypes.none)
gp.AddRectangle(rct);
if (_borderType == BorderTypes.square)
{
gp.AddRectangle(rct);
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), rct);
}
if (_borderType == BorderTypes.rounded)
{
Rectangle arcRct = new Rectangle(rct.X, rct.Y, ArcWidth, ArcWidth);
Point pt1 = new Point(rct.X + ArcWidth, rct.Y);
Point pt2 = new Point(rct.X + rct.Width - ArcWidth, rct.Y);
gp.AddArc(arcRct, 180, 90);
gp.AddLine(pt1, pt2);
arcRct.Location = pt2;
gp.AddArc(arcRct, 270, 90);
pt1 = new Point(rct.X + rct.Width, rct.Y + ArcWidth);
pt2 = new Point(rct.X + rct.Width, rct.Y + rct.Height - ArcWidth);
gp.AddLine(pt1, pt2);
arcRct.Y = pt2.Y;
gp.AddArc(arcRct, 0, 90);
pt1 = new Point(rct.X + rct.Width - ArcWidth, rct.Y + rct.Height);
pt2 = new Point(rct.X + ArcWidth, rct.Y + rct.Height);
gp.AddLine(pt1, pt2);
arcRct.X = rct.X;
gp.AddArc(arcRct, 90, 90);
gp.CloseFigure();
e.Graphics.DrawPath(new Pen(Color.Black, 1), gp);
}
return gp;
}
#endregion
}
}