C# WinForm控件美化之ImageComboBox

时间:2013-06-29 09:27:18
【文件属性】:

文件名称:C# WinForm控件美化之ImageComboBox

文件大小:780KB

文件格式:RAR

更新时间:2013-06-29 09:27:18

c#

这篇文章中我们重点需要实现的是(3)、(4)两项功能,下面我们来介绍具体实现的方法。 第一步,实现ImageComboBoxItem类。 要实现显示图标,当然要给每个项添加与图标相关的信息了,ImageComboBoxItem类应该包括以下内容:文本(Text)、缩进的级别(Level)、图标的索引(ImageIndex、ImageKey),用户数据(Tag)。ImageComboBoxItem类实现了ISerializable接口,实现自定义序列化。ImageComboBoxItem类的类视图如下: 图3 ImageComboxItem类视图 ImageComboBoxItem类的代码如下: [Serializable] [DefaultProperty("Text")] [TypeConverter( typeof(ExpandableObjectConverter))] public class ImageComboBoxItem : IDisposable, ISerializable ...{ Fields#region Fields private ImageComboBox _imageComboBox; private string _text = "ImageComboBoxItem"; private ImageComboBoxItemImageIndexer _imageIndexer; private object _tag; private int _level; #endregion Constructors#region Constructors public ImageComboBoxItem() ...{ } public ImageComboBoxItem(string text) : this(text, -1, 0) ...{ } public ImageComboBoxItem( string text, int imageIndex) : this(text, imageIndex, 0) ...{ } public ImageComboBoxItem( string text, string imageKey) : this(text, imageKey, 0) ...{ } public ImageComboBoxItem( string text, int imageIndex, int level) : this() ...{ _text = text; ImageIndexer.Index = imageIndex; _level = level; } public ImageComboBoxItem( string text, string imageKey, int level) : this() ...{ _text = text; ImageIndexer.Key = imageKey; _level = level; } protected ImageComboBoxItem( SerializationInfo info, StreamingContext context) : this() ...{ Deserialize(info, context); } #endregion Properties#region Properties [Localizable(true)] public string Text ...{ get ...{ if (_text != null) ...{ return _text; } return ""; } set ...{ _text = value; } } [Bindable(true)] [Localizable(false)] [DefaultValue("")] [TypeConverter(typeof(StringConverter))] [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)] public object Tag ...{ get ...{ return _tag; } set ...{ _tag = value; } } [DefaultValue(0)] [Localizable(true)] [RefreshProperties(RefreshProperties.Repaint)] [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)] public int Level ...{ get ...{ return _level; } set ...{ if (_level < 0) ...{ throw new ArgumentOutOfRangeException("level"); } _level = value; } } [DefaultValue(-1)] [Localizable(true)] [RelatedImageList("ImageComboBox.ImageList")] [Editor( EditorAssemblyName.ImageIndexEditor, typeof(UITypeEditor))] [RefreshProperties(RefreshProperties.Repaint)] [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)] [TypeConverter(typeof(NoneExcludedImageIndexConverter))] public int ImageIndex ...{ get ...{ if (((ImageIndexer.Index != -1) && (ImageList != null)) && (ImageIndexer.Index >= ImageList.Images.Count)) ...{ return ImageList.Images.Count - 1; } return ImageIndexer.Index; } set ...{ if (value < -1) ...{ throw new ArgumentOutOfRangeException("ImageIndex"); } ImageIndexer.Index = value; } } [DefaultValue("")] [Localizable(true)] [RelatedImageList("ImageComboBox.ImageList")] [RefreshProperties(RefreshProperties.Repaint)] [Editor( EditorAssemblyName.ImageIndexEditor, typeof(UITypeEditor))] [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)] [TypeConverter(typeof(ImageKeyConverter))] public string ImageKey ...{ get ...{ return ImageIndexer.Key; } set ...{ ImageIndexer.Key = value; } } [Browsable(false)] public ImageComboBox ImageComboBox ...{ get ...{ return _imageComboBox; } } internal Image Image ...{ get ...{ int actualIndex = ImageIndexer.ActualIndex; if (ImageList != null && ImageList.Images.Count > 0 && actualIndex != -1) ...{ return ImageList.Images[actualIndex]; } return null; } } [Browsable(false)] internal ImageList ImageList ...{ get ...{ if (ImageComboBox != null) ...{ return ImageComboBox.ImageList; } return null; } } internal ImageComboBoxItemImageIndexer ImageIndexer ...{ get ...{ if (_imageIndexer == null) ...{ _imageIndexer = new ImageComboBoxItemImageIndexer(this); } return _imageIndexer; } } #endregion Methods#region Methods public override string ToString() ...{ return _text; } internal void Host(ImageComboBox parent) ...{ _imageComboBox = parent; } [SecurityPermission( SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter), SecurityPermission( SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] protected virtual void Serialize( SerializationInfo info, StreamingContext context) ...{ info.AddValue("Text", Text); info.AddValue("Level", Level); info.AddValue("ImageIndex", ImageIndexer.Index); if (!string.IsNullOrEmpty(ImageIndexer.Key)) ...{ info.AddValue("ImageKey", ImageIndexer.Key); } } protected virtual void Deserialize( SerializationInfo info, StreamingContext context) ...{ string imageKey = null; int imageIndex = -1; SerializationInfoEnumerator enumerator = info.GetEnumerator(); while (enumerator.MoveNext()) ...{ SerializationEntry current = enumerator.Current; if (current.Name == "Text") ...{ Text = info.GetString(current.Name); } else if (current.Name == "Level") ...{ Level = info.GetInt32(current.Name); } else ...{ if (current.Name == "ImageIndex") ...{ imageIndex = info.GetInt32(current.Name); continue; } if (current.Name == "ImageKey") ...{ imageKey = info.GetString(current.Name); continue; } } } if (imageKey != null) ...{ ImageKey = imageKey; } else if (imageIndex != -1) ...{ ImageIndex = imageIndex; } } #endregion ISerializable 成员#region ISerializable 成员 [SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData( SerializationInfo info, StreamingContext context) ...{ Serialize(info, context); } #endregion IDisposable 成员#region IDisposable 成员 public void Dispose() ...{ _imageComboBox = null; _imageIndexer = null; _tag = null; } #endregion ImageComboBoxItemImageIndexer Class #region ImageComboBoxItemImageIndexer Class internal class ImageComboBoxItemImageIndexer : ImageIndexer ...{ private ImageComboBoxItem _owner; public ImageComboBoxItemImageIndexer( ImageComboBoxItem owner) ...{ _owner = owner; } public override ImageList ImageList ...{ get ...{ if (_owner != null) ...{ return _owner.ImageList; } return null; } set ...{ } } } #endregion }第二步,实现ImageComboBoxItemCollection类。 ImageComboBoxItemCollection类实现跟ComboBox.ObjectCollection类一样的功能,用来代替ComboBox控件中ComboBox.ObjectCollection类,定义一个新的Items来存储ImageComboBoxItem对象,来实现ImageComboBox控件设计时可以支持ImageComboBoxItem对象的设计。 第三步,给ImageComboBox控件添加一些属性。 ImageComboBox控件主要需要添加几个属性:图标集合(ImageList)、没有选择项时ComboBox中显示的默认图标(DefaultImage)、缩进值(Indent)、提示信息(EmptyTextTip)、提示信息的文本颜色(EmptyTextTipColor)。还需要覆盖一些属性,这里不一一列出了,看下面的ImageComboBox控件的类视图: 图4 ImageComboBox类视图 第四步,实现EditorNativeWimdow类。 EditorNativeWimdow类的主要功能是实现当ImageComboBox控件的列表模式设为非DropDownList的时候,即DropDownStyle不是ComboBoxStyle.DropDownList的时候,实现在Editor中绘制图标。EditorNativeWimdow类的代码如下: private class EditorNativeWimdow : NativeWindow, IDisposable ...{ Fields#region Fields private ImageComboBox _owner; private const int EC_LEFTMARGIN = 0x1; private const int EC_RIGHTMARGIN = 0x2; private const int EC_USEFONTINFO = 0xFFFF; private const int EM_SETMARGINS = 0xD3; private const int EM_GETMARGINS = 0xD4; #endregion Constructors#region Constructors public EditorNativeWimdow( ImageComboBox owner) : base() ...{ _owner = owner; Attach(); } #endregion Private Methods#region Private Methods private void Attach() ...{ if (!Handle.Equals(IntPtr.Zero)) ...{ ReleaseHandle(); } AssignHandle(_owner.EditHandle); SetMargin(); } protected override void WndProc( ref Message m) ...{ base.WndProc(ref m); switch (m.Msg) ...{ case (int)NativeMethods.WindowsMessage.WM_SETFONT: SetMargin(); break; case (int)NativeMethods.WindowsMessage.WM_PAINT: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_SETFOCUS: case (int)NativeMethods.WindowsMessage.WM_KILLFOCUS: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_LBUTTONDOWN: case (int)NativeMethods.WindowsMessage.WM_RBUTTONDOWN: case (int)NativeMethods.WindowsMessage.WM_MBUTTONDOWN: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_LBUTTONUP: case (int)NativeMethods.WindowsMessage.WM_RBUTTONUP: case (int)NativeMethods.WindowsMessage.WM_MBUTTONUP: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_LBUTTONDBLCLK: case (int)NativeMethods.WindowsMessage.WM_RBUTTONDBLCLK: case (int)NativeMethods.WindowsMessage.WM_MBUTTONDBLCLK: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_KEYDOWN: case (int)NativeMethods.WindowsMessage.WM_CHAR: case (int)NativeMethods.WindowsMessage.WM_KEYUP: RePaint(); break; case (int)NativeMethods.WindowsMessage.WM_MOUSEMOVE: if (!m.WParam.Equals(IntPtr.Zero)) ...{ RePaint(); } break; } } internal void SetMargin() ...{ NearMargin(Handle, _owner.ItemHeight + 5); } private static bool IsRightToLeft( IntPtr handle) ...{ int style = NativeMethods.GetWindowLong( handle, (int)NativeMethods.GWL.GWL_EXSTYLE); return ( ((style & (int)NativeMethods.WS_EX.WS_EX_RIGHT) == (int)NativeMethods.WS_EX.WS_EX_RIGHT) || ((style & (int)NativeMethods.WS_EX.WS_EX_RTLREADING) == (int)NativeMethods.WS_EX.WS_EX_RTLREADING) || ((style & (int)NativeMethods.WS_EX.WS_EX_LEFTSCROLLBAR) == (int)NativeMethods.WS_EX.WS_EX_LEFTSCROLLBAR)); } private static void FarMargin( IntPtr handle, int margin) ...{ int message = IsRightToLeft(handle) ? EC_LEFTMARGIN : EC_RIGHTMARGIN; if (message == EC_LEFTMARGIN) ...{ margin = margin & 0xFFFF; } else ...{ margin = margin * 0x10000; } NativeMethods.SendMessage( handle, EM_SETMARGINS, message, margin); } internal static void NearMargin( IntPtr handle, int margin) ...{ int message = IsRightToLeft(handle) ? EC_RIGHTMARGIN : EC_LEFTMARGIN; if (message == EC_LEFTMARGIN) ...{ margin = margin & 0xFFFF; } else ...{ margin = margin * 0x10000; } NativeMethods.SendMessage( handle, EM_SETMARGINS, message, margin); } private void RePaint() ...{ ImageComboBoxItem item = _owner.SelectedItem; NativeMethods.RECT rcClient = new NativeMethods.RECT(); NativeMethods.GetClientRect(Handle, ref rcClient); bool rightToLeft = IsRightToLeft(Handle); IntPtr handle = Handle; IntPtr hdc = NativeMethods.GetDC(handle); if (hdc == IntPtr.Zero) ...{ return; } try ...{ using (Graphics g = Graphics.FromHdc(hdc)) ...{ int itemSize = _owner.ItemHeight; Rectangle imageRect = new Rectangle( 0, rcClient.Top + (rcClient.Bottom - itemSize) / 2, itemSize, itemSize); Rectangle textRect = new Rectangle( 0, 0, rcClient.Right - itemSize - 6, rcClient.Bottom); if (rightToLeft) ...{ imageRect.X = rcClient.Right - itemSize - 2; textRect.X = 2; } else ...{ imageRect.X = 2; textRect.X = imageRect.Right + 2; } if (_owner.Text.Length == 0) ...{ DrawImage( g, imageRect, _owner.DefaultImage, _owner.DefaultImageList, 0, _owner.Focused); if (_owner.Text.Length == 0 && !string.IsNullOrEmpty(_owner.EmptyTextTip) && !_owner.Focused) ...{ TextFormatFlags format = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter; if (_owner.RightToLeft == RightToLeft.Yes) ...{ format |= (TextFormatFlags.RightToLeft | TextFormatFlags.Right); } TextRenderer.DrawText( g, _owner.EmptyTextTip, _owner.Font, textRect, _owner.EmptyTextTipColor, format); } return; } if (_owner.Text.Length > 0) ...{ using (SolidBrush brush = new SolidBrush(_owner.BackColor)) ...{ g.FillRectangle(brush, imageRect); } } if (_owner.Items.Count == 0) ...{ DrawImage( g, imageRect, _owner.DefaultImage, _owner.DefaultImageList, 0, _owner.Focused); return; } if (item == null) ...{ return; } DrawImage( g, imageRect, item.Image, _owner.ImageList, item.ImageIndexer.ActualIndex, _owner.Focused); } } finally ...{ NativeMethods.ReleaseDC(handle, hdc); } } private void DrawImage( Graphics g, Rectangle imageRect, Image image, ImageList imageList, int imageIndex, bool focus) ...{ using (SolidBrush brush = new SolidBrush(_owner.BackColor)) ...{ g.FillRectangle(brush, imageRect); } if (image == null) ...{ return; } using (InterpolationModeGraphics graphics = new InterpolationModeGraphics( g, InterpolationMode.HighQualityBicubic)) ...{ if (focus) ...{ IntPtr hIcon = NativeMethods.ImageList_GetIcon( imageList.Handle, imageIndex, (int)NativeMethods.ImageListDrawFlags.ILD_SELECTED); g.DrawIcon(Icon.FromHandle(hIcon), imageRect); NativeMethods.DestroyIcon(hIcon); } else ...{ g.DrawImage( image, imageRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); } } } #endregion IDisposable 成员#region IDisposable 成员 public void Dispose() ...{ _owner = null; base.ReleaseHandle(); } #endregion }第五步,重写OnCreateControl、OnHandleDestroyed方法。 重写这两个方法主要是为了ImageComboBox控件的DropDownStyle为不同的值时,控制是否需要在Editor中绘制图标,这两个方法的代码如下: protected override void OnCreateControl() ...{ base.OnCreateControl(); if (DropDownStyle != ComboBoxStyle.DropDownList && !DesignMode) ...{ if (_nativeWimdow == null) ...{ _nativeWimdow = new EditorNativeWimdow(this); } } } protected override void OnHandleDestroyed(EventArgs e) ...{ if (_nativeWimdow != null) ...{ _nativeWimdow.Dispose(); _nativeWimdow = null; } base.OnHandleDestroyed(e); }第六步,重写OnDropDown方法。 重写这个方法是为了实现调节下拉列表框显示的大小,因为画了图标,以免项显示不完全。OnDropDown方法代码如下: protected override void OnDropDown( EventArgs e) ...{ base.OnDropDown(e); int ddWidth = 0; int textWidth = 0; int itemWidth = 0; int scrollBarWidth = Items.Count > MaxDropDownItems ? SystemInformation.VerticalScrollBarWidth : 0; Graphics g = CreateGraphics(); foreach (ImageComboBoxItem item in Items) ...{ textWidth = g.MeasureString( item.Text, Font).ToSize().Width; itemWidth = textWidth + ItemHeight + 8 + _indent * item.Level + scrollBarWidth; if (itemWidth > ddWidth) ddWidth = itemWidth; } DropDownWidth = (ddWidth > Width) ? ddWidth : Width; g.Dispose(); } 第七步,重绘列表项,让其缩进和显示图标。 重绘列表项,需要把ImageComboBox控件的DrawMode设为DrawMode.OwnerDrawFixed,然后通过重写OnDrawItem方法实现,具体代码如下: protected override void OnDrawItem(DrawItemEventArgs e) ...{ if (e.Index != -1) ...{ ImageComboBoxItem item = Items[e.Index]; Graphics g = e.Graphics; Rectangle bounds = e.Bounds; int indentOffset = Indent * item.Level; if ((e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit) ...{ indentOffset = 0; } int imageWidth = bounds.Height; Rectangle imageRect; Rectangle textRect; TextFormatFlags format = TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordBreak; imageRect = new Rectangle( bounds.Left + indentOffset + 2, bounds.Top, imageWidth, imageWidth); textRect = new Rectangle( imageRect.Right + 3, bounds.Y, bounds.Width - imageRect.Width - indentOffset - 5, bounds.Height); Rectangle backRect = new Rectangle( textRect.X, textRect.Y + 1, textRect.Width, textRect.Height - 2); backRect.Width = TextRenderer.MeasureText( item.Text, e.Font, textRect.Size, format).Width; if (base.RightToLeft == RightToLeft.Yes) ...{ imageRect.X = bounds.Right - imageRect.Right; textRect.X = bounds.Right - textRect.Right; backRect.X = textRect.Right - backRect.Width; } bool selected = ((e.State & DrawItemState.Selected) == DrawItemState.Selected); Color backColor = selected ? SystemColors.Highlight : base.BackColor; using (Brush backBrush = new SolidBrush(backColor)) ...{ g.FillRectangle(backBrush, backRect); } if (selected) ...{ ControlPaint.DrawFocusRectangle( g, backRect); } Image image = item.Image; if (image != null) ...{ using (InterpolationModeGraphics graphics = new InterpolationModeGraphics( g, InterpolationMode.HighQualityBicubic)) ...{ if (selected) ...{ IntPtr hIcon = NativeMethods.ImageList_GetIcon( ImageList.Handle, item.ImageIndexer.ActualIndex, (int)NativeMethods.ImageListDrawFlags.ILD_SELECTED); g.DrawIcon(Icon.FromHandle(hIcon), imageRect); NativeMethods.DestroyIcon(hIcon); } else ...{ g.DrawImage( image, imageRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel); } } } TextRenderer.DrawText( g, item.Text, e.Font, textRect, base.ForeColor, format); } } 到此为止,ImageComboBox控件需要实现的功能就完成了。


【文件预览】:
ImageComboBoxDemo
----ImageComboBoxDemo.sln(1KB)
----ComboBoxEx()
--------EditorAssemblyName.cs(340B)
--------bin()
--------ComboBoxEx.cs(13KB)
--------obj()
--------RoundStyle.cs(1KB)
--------ControlState.cs(891B)
--------ImageIndexer.cs(1KB)
--------RenderHelper.cs(9KB)
--------Properties()
--------SmoothingModeGraphics.cs(1KB)
--------InterpolationModeGraphics.cs(1KB)
--------ImageComboBox.cs(29KB)
--------GraphicsPathHelper.cs(6KB)
--------ImageComboBoxItem.cs(10KB)
--------TextRenderingHintGraphics.cs(1KB)
--------ComboBoxEx.csproj(3KB)
--------ControlPaintEx.cs(10KB)
--------NoneExcludedImageIndexConverter.cs(341B)
--------ClassDiagram1.cd(1B)
--------NativeMethods.cs(11KB)
----ImageComboBoxDemo1.png(26KB)
----ImageComboBoxDemo.png(23KB)
----ImageComboBoxClassCD.png(13KB)
----ImageComboBoxDemo()
--------AboutBoxCSharpWinDemo.cs(6KB)
--------AboutBoxCSharpWinDemo.resx(24KB)
--------bin()
--------obj()
--------Resources()
--------Properties()
--------Pic()
--------Program.cs(484B)
--------ImageComboBoxDemo.csproj(5KB)
--------FormCSharpWinDemo.resx(36KB)
--------FormCSharpWinDemo.cs(3KB)
--------AboutBoxCSharpWinDemo.Designer.cs(13KB)
--------FormCSharpWinDemo.Designer.cs(14KB)
--------dll()
----ImageComboBoxDemo.suo(49KB)
----ImageComboBoxItemClassCD.png(14KB)

网友评论

  • 下载下来用了一下,能用,还可以自己再优化下。
  • 很好用~~~~
  • 还行吧,没想象中那么美化,色彩变化而已。
  • 感觉有点复杂,之前看到一个相对简单的。不过还是学习的好例子!
  • 有点复杂,但还能用
  • 功能很好,不过感觉实现好复杂,不过可以作为参考学习
  • 控件能够使用,但是使用比较麻烦
  • 额……控件能够使用,但是使用比较麻烦,且不美观。 但基本能够达到目的