利用IExtenderProvider扩展控件属性
1、首先要给类添加属性:
[ProvideProperty( "MenuImage", typeof(Component)) ]
用于提供给其他控件的属性名称
[DefaultProperty("ImageList")]
用于提供Extender的默认属性
2、要继承Component, IExtenderProvider两个类(接口)
IExtenderProvider接口有一个方法要进行实现:
CanExtend() : 指定此对象是否可以将其扩展程序属性提供给指定的对象。
另外还需要添加两个其他的公用方法
SetPropertyName():为每个实例设置属性值
GetPropertyName():得到控件实例的属性值
3、需要定义一个HashTable,用于存储控件的属性值。其中键存放的是控件的名称, 值存放的是控件的属性值.
4、在SetPropertyName()方法中, 可以设置控件的事件
5、在构造函数中, 添加如下代码, 用于设计模式
public PropertyName(System.ComponentModel.IContainer container)
{
container.Add(this);
}
好了, 以上是我自己对这个接口的理解, 下面我把学习用的代码贴出来以供参考
using System ;
using System.ComponentModel ;
using System.Collections ;
using System.Diagnostics ;
using System.Windows.Forms ;
using System.Drawing ;
using System.Drawing.Drawing2D ;
namespace cn.everx.MenuImageLib
{
[ProvideProperty( "MenuImage", typeof(Component)) ]
[DefaultProperty("ImageList")]
[ToolboxBitmap(typeof(MenuImage),"cn.everx.MenuImageLib.OfficeMenus.bmp")]
//上面这个属性没有作用
public class MenuImage : Component, IExtenderProvider
{
#region Private Attributes
const int IMAGE_BUFFER = 25 ;
const int SHORTCUT_RIGHTBUFFER = 10 ;
const int SHORTCUT_BUFFER = 25 ;
int IMAGE_WIDTH = SystemInformation.SmallIconSize.Width ;
int IMAGE_HEIGHT = SystemInformation.SmallIconSize.Height ;
Hashtable _hashTable = new Hashtable( );
ImageList _imageList = null;
#endregion
#region Constructors
public MenuImage(System.ComponentModel.IContainer container)
{
container.Add(this);
}
public MenuImage()
{
}
#endregion
#region Public Members
public void SetMenuImage( Component component, string indexValue )
{
// check if the string value is null, or not numeric
// automatically throws and error if not during the convert
if (indexValue != null)
if ( indexValue.Length > 0 )
{
uint imageIndex = Convert.ToUInt16( indexValue ) ;
}
// store the menuitem and related index in the local hashtable
if ( _hashTable.Contains( component ) != true)
{
_hashTable.Add( component, indexValue ) ;
MenuItem menuItem = (MenuItem) component ;
// set the menu to owner drawn
menuItem.OwnerDraw = true ; //这句很重要,因为需要进行菜单的自定义重绘
// hook up the menu owner drawn events
menuItem.MeasureItem += new MeasureItemEventHandler( OnMeasureItem ) ;
menuItem.DrawItem += new DrawItemEventHandler( OnDrawItem ) ;
}
else
{
_hashTable [ component ] = indexValue ;
}
}
public string GetMenuImage( Component component )
{
if( _hashTable.Contains( component ))
return (string) _hashTable[ component ] ;
return null;
}
public bool CanExtend( object component )
{
// only support MenuItem objects that are not
// top-level menus (default rendering for top-level
// menus is fine - does not need extension
if ( component is MenuItem )
{
MenuItem menuItem = (MenuItem) component ;
return ! ( menuItem.Parent is MainMenu ) ;
}
return false ;
}
public ImageList ImageList
{
get{ return _imageList ; }
set{ _imageList = value; }
}
#endregion
#region 私有方法 / 帮助类
private int GetMenuImageIndex( Object sender )
{
string menuImageValue = this.GetMenuImage( sender as Component ) ;
if ( _imageList != null)
if ( menuImageValue != null )
if ( menuImageValue.Length >= 0 )
{
int imageIndex = Convert.ToInt32( menuImageValue ) ;
if ( imageIndex >= 0 && imageIndex < _imageList.Images.Count )
return imageIndex ;
}
return -1 ;
}
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, _imageList ) ;
// calculate the menu height
e.ItemHeight = menuHelper.CalcHeight() ;
e.ItemWidth = menuHelper.CalcWidth() ;
}
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, _imageList ) ;
// 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
{
int imageIndex = this.GetMenuImageIndex( sender ) ;
menuHelper.DrawMenu( e.Bounds, menuSelected, imageIndex ) ;
}
}
#endregion
#region MenuHelper Class
private class MenuHelper
{
#region Private Attributes
// 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 = 1 ;
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 + 10 ;
// holds the local instances of the MenuItem and Graphics
// objects passed in through the Constructor
MenuItem _menuItem = null ;
Graphics _graphics = null ;
ImageList _imageList = null ;
#endregion
#region Constructors
public MenuHelper( MenuItem menuItem, Graphics graphics, ImageList imageList )
{
_menuItem = menuItem ;
_graphics = graphics ;
_imageList = imageList ;
}
#endregion
#region Public Members
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 ;
}
}
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 ;
}
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 == "-" ) ;
}
public bool IsTopLevel()
{
return ( _menuItem.Parent is MainMenu ) ;
}
public string ShortcutText
{
get
{
if ( _menuItem.ShowShortcut == true && _menuItem.Shortcut != Shortcut.None )
{
Keys keys = (Keys) _menuItem.Shortcut ;
return Convert.ToChar(Keys.Tab) + System.ComponentModel.TypeDescriptor.GetConverter(keys.GetType()).ConvertToString(keys) ;
}
return null ;
}
}
public void DrawMenu ( Rectangle bounds, bool selected, int indexValue )
{
// 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 )
{
Image menuImage = null ;
System.IO.Stream stream = this.GetType().Assembly.GetManifestResourceStream("cn.everx.MenuImageLib.SubItem16.ico") ;
menuImage = Image.FromStream(stream) ;
this.DrawArrow( menuImage, 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 ( indexValue > -1 )
{
Image menuImage = null ;
menuImage = _imageList.Images[indexValue] ;
DrawImage( menuImage, bounds ) ;
}
}
}
public void DrawBackground( Rectangle bounds, bool selected )
{
if (selected)
{
Brush b = new SolidBrush(Color.FromArgb(255, 238, 194));
_graphics.FillRectangle(b, bounds);
_graphics.DrawRectangle(new
pen (SystemColors.ActiveCaption,1),bounds.X,bounds.Y,bounds.Width-1,bounds.Height-1);
}
else
{
_graphics.FillRectangle(new SolidBrush(Color.White),bounds);
// the picture area rectangle
Rectangle rect = new Rectangle(bounds.X-1,
bounds.Y,
IMAGE_BUFFER_SIZE - 3,
bounds.Height);
// Create Gradient brush, using system colors
Brush b = new LinearGradientBrush(rect,
Color.FromArgb(30,SystemColors.InactiveCaption),
SystemColors.ActiveCaption,
false);
// Draw the rect
_graphics.FillRectangle(b, rect);
}
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) ;
}
#endregion
#region Private Members
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
{
menuBrush = new SolidBrush( SystemColors.MenuText ) ;
}
// draw the menu text
StringFormat sfMenu = new StringFormat() ;
sfMenu.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show ;
_graphics.DrawString( _menuItem.Text, menuFont, menuBrush, bounds.Left + IMAGE_BUFFER_SIZE, 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);
}
}
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 ) ;
}
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 ) ;
}
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 ) ;
}
#endregion
}
#endregion
}
}
注:以上代码是我利用CodeProject上的代码经自己稍微修改作成的