http://www.csharpwin.com/csharpresource/5261r2896.shtml
WinForm程序开发中,ListBox控件是比较常用的一个控件,有时候我们需要一个比较美观的ListBox控件,让用户看ListBox控件显示的信息时比较清晰、形象,我们可以让ListBox控件隔行显示不同的背景色,让每个项显示图标,本文将介绍怎样实现这样的一个ListBox扩展控件。
先来看看最终实现ListBox扩展控件的效果图:
下面我们一步步的来实现这个ListBox扩展控件。首先,我们来明确一下需要完成的功能:
1、实现ListBox的项隔行显示不同的背景色。
2、扩展ListBox的项,让它可以显示图标。
3、实现可以更换ListBox边框的颜色。
第一项功能在以前我写的一篇文章《C# WinForm控件美化扩展系列之ListBox》中已经介绍过,这里就不介绍了,我们重点介绍后两个功能的实现。
要让ListBox的每个项显示图标,首先我们需要定义一个我们自己的ListBoxExItem类对象来代替原来的ListBox项,ListBoxExItem需要包含三个属性:Image,我们的图标;Text,显示的文本;Tag,项的用户自定义数据。下面看看这个对象的代码:
[Serializable]
[TypeConverter(typeof(ExpandableObjectConverter))]
public classListBoxExItem : IDisposable
{
FieldsFields
private string_text = "ListBoxExItem";
privateImage _image;
private object_tag;
#endregion
ConstructorsConstructors
publicListBoxExItem()
{
}
publicListBoxExItem(stringtext)
: this(text, null)
{
}
publicListBoxExItem(stringtext, Image image)
{
_text =text;
_image =image;
}
#endregion
PropertiesProperties
[DefaultValue("ImageComboBoxItem")]
[Localizable(true)]
public stringText
{
get { return_text; }
set { _text =value; }
}
[DefaultValue(typeof(Image), "null")]
publicImage Image
{
get { return_image; }
set { _image =value; }
}
[Bindable(true)]
[Localizable(false)]
[DefaultValue("")]
[TypeConverter(typeof(StringConverter))]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Hidden)]
public objectTag
{
get { return_tag; }
set { _tag =value; }
}
#endregion
Override MethodsOverride Methods
public override stringToString()
{
return_text;
}
#endregion
IDisposable 成员IDisposable 成员
public voidDispose()
{
_image = null;
_tag = null;
}
#endregion
}
实现了ListBoxExItem类对象,我们还需要实现一个ListBoxExItemCollection集合,这个集合需要实现IList、ICollection和IEnumerable接口,用来存储ListBoxExItem对象,像ListBox自己实现的ListBox.ObjectCollection集合那样,我们需要实现一些添加项、删除项等等的一些方法,看看这个集合类的视图和代码:
ListBoxExItemCollection 类视图
ListBoxExItemCollection 类源码:
[ListBindable(false)]
public classListBoxExItemCollection
: IList,ICollection,IEnumerable
{
FieldsFields
privateListBoxEx _owner;
#endregion
ConstructorsConstructors
publicListBoxExItemCollection(ListBoxEx owner)
{
_owner =owner;
}
#endregion
PropertiesProperties
internalListBoxEx Owner
{
get { return_owner; }
}
publicListBoxExItem this[intindex]
{
get { returnOwner.OldItems[index] asListBoxExItem; }
set { Owner.OldItems[index] =value; }
}
public intCount
{
get { returnOwner.OldItems.Count; }
}
public boolIsReadOnly
{
get { returnOwner.OldItems.IsReadOnly; }
}
#endregion
Public MethodsPublic Methods
public intAdd(ListBoxExItem item)
{
if(item == null)
{
throw newArgumentNullException("item");
}
returnOwner.OldItems.Add(item);
}
public voidAddRange(ListBoxExItemCollection value)
{
foreach(ListBoxExItem item invalue)
{
Add(item);
}
}
public voidAddRange(ListBoxExItem[] items)
{
Owner.OldItems.AddRange(items);
}
public voidClear()
{
Owner.OldItems.Clear();
}
public boolContains(ListBoxExItem item)
{
returnOwner.OldItems.Contains(item);
}
public voidCopyTo(
ListBoxExItem[] destination,
intarrayIndex)
{
Owner.OldItems.CopyTo(destination, arrayIndex);
}
public intIndexOf(ListBoxExItem item)
{
returnOwner.OldItems.IndexOf(item);
}
public voidInsert(intindex, ListBoxExItem item)
{
if(item == null)
{
throw newArgumentNullException("item");
}
Owner.OldItems.Insert(index, item);
}
public voidRemove(ListBoxExItem item)
{
Owner.OldItems.Remove(item);
}
public voidRemoveAt(intindex)
{
Owner.OldItems.RemoveAt(index);
}
publicIEnumerator GetEnumerator()
{
returnOwner.OldItems.GetEnumerator();
}
#endregion
IList 成员IList 成员
intIList.Add(objectvalue)
{
if(!(value isListBoxExItem))
{
throw newArgumentException();
}
returnAdd(value asListBoxExItem);
}
voidIList.Clear()
{
Clear();
}
boolIList.Contains(objectvalue)
{
returnContains(value asListBoxExItem);
}
intIList.IndexOf(objectvalue)
{
returnIndexOf(value asListBoxExItem);
}
voidIList.Insert(intindex, objectvalue)
{
if(!(value isListBoxExItem))
{
throw newArgumentException();
}
Insert(index, value asListBoxExItem);
}
boolIList.IsFixedSize
{
get { return false; }
}
boolIList.IsReadOnly
{
get { returnIsReadOnly; }
}
voidIList.Remove(objectvalue)
{
Remove(value asListBoxExItem);
}
voidIList.RemoveAt(intindex)
{
RemoveAt(index);
}
objectIList.this[intindex]
{
get
{
return this[index];
}
set
{
if(!(value isListBoxExItem))
{
throw newArgumentException();
}
this[index] =value asListBoxExItem;
}
}
#endregion
ICollection 成员ICollection 成员
voidICollection.CopyTo(Array array, intindex)
{
CopyTo((ListBoxExItem[])array, index);
}
intICollection.Count
{
get { returnCount; }
}
boolICollection.IsSynchronized
{
get { return false; }
}
objectICollection.SyncRoot
{
get { return this; }
}
#endregion
IEnumerable 成员IEnumerable 成员
IEnumerator IEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
#endregion
}
准备工作做好了,现在我们来动一动ListBox了,先给它加一个OldItems属性,表示的是它原来的Items属性,然后我们需要用我们自己定义的ListBoxExItemCollection集合来定义一个Items属性,覆盖原来的Items属性,这样在设计的时候就是设计我们自己定义的ListBoxExItem项了,我们就可以设置我们自己的项的图标和文本了。看看这两个属性的定义:
internalListBox.ObjectCollection OldItems
{
get { return base.Items; }
}
[Localizable(true)]
[MergableProperty(false)]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Content)]
public newListBoxExItemCollection Items
{
get { return_items; }
}
最后就是DrawItem了,我们直接用我们自己自定义的Items就可以获取需要绘制的ListBoxExItem项了,我们把图标和文本绘好就OK了。看看绘制的代码:
protected override voidOnDrawItem(DrawItemEventArgs e)
{
base.OnDrawItem(e);
if(e.Index != -1 && base.Items.Count > 0)
{
System.Diagnostics.Debug.WriteLine(e.State);
Rectangle bounds =e.Bounds;
ListBoxExItem item =Items[e.Index];
Graphics g =e.Graphics;
if((e.State &DrawItemState.Selected)
==DrawItemState.Selected)
{
RenderBackgroundInternal(
g,
bounds,
_selectedColor,
_selectedColor,
Color.FromArgb(200, 255, 255, 255),
0.45f,
true,
LinearGradientMode.Vertical);
}
else
{
Color backColor;
if(e.Index % 2 == 0)
{
backColor =_rowBackColor2;
}
else
{
backColor =_rowBackColor1;
}
using(SolidBrush brush = newSolidBrush(backColor))
{
g.FillRectangle(brush, bounds);
}
}
Image image =item.Image;
Rectangle imageRect = newRectangle(
bounds.X + 2,
bounds.Y + 2,
bounds.Height - 4,
bounds.Height - 4);
Rectangle textRect = newRectangle(
imageRect.Right + 2,
bounds.Y,
bounds.Width -imageRect.Right - 2,
bounds.Height);
stringtext =item.ToString();
TextFormatFlags formatFlags =
TextFormatFlags.VerticalCenter;
if(RightToLeft ==RightToLeft.Yes)
{
imageRect.X =bounds.Right -imageRect.Right;
textRect.X =bounds.Right -textRect.Right;
formatFlags |=TextFormatFlags.RightToLeft;
formatFlags |=TextFormatFlags.Right;
}
else
{
formatFlags |=TextFormatFlags.Left;
}
if(image != null)
{
g.InterpolationMode =
InterpolationMode.HighQualityBilinear;
g.DrawImage(
image,
imageRect,
0,
0,
image.Width,
image.Height,
GraphicsUnit.Pixel);
}
TextRenderer.DrawText(
g,
text,
Font,
textRect,
ForeColor,
v formatFlags);
if((e.State &DrawItemState.Focus) ==
DrawItemState.Focus)
{
e.DrawFocusRectangle();
}
}
}
接下来开始换ListBox控件的边框颜色,这个功能需要用到一些API函数,简单的介绍一下实现的原理,首先我们需要获取ListBox控件非客户区的区域,这个需要注意的地方是需要排除滚动条的区域,应为这个区域系统会自己绘制滚动条的,我们不需要处理。获取非客户区域后,我们就用ListBox控件的背景色来填充它,然后再绘制边框,这些绘制需要在WM_NCPAINT消息中绘制,具体的实现大家可以下载源码看,由于涉及的源码比较多,这里就不贴源码了。
好了,现在整个ListBox控件的扩展和美化就完成了,希望你能喜欢,也希望对你有所帮助。最后希望大家继续支持CS程序员之窗。