listbox美化
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 class ListBoxExItem : IDisposable
{
Fields Fields
private string _text = "ListBoxExItem";
private Image _image;
private object _tag;
#endregion
Constructors Constructors
public ListBoxExItem()
{
}
public ListBoxExItem(string text)
: this(text, null)
{
}
public ListBoxExItem(string text, Image image)
{
_text = text;
_image = image;
}
#endregion
Properties Properties
[DefaultValue("ImageComboBoxItem")]
[Localizable(true)]
public string Text
{
get { return _text; }
set { _text = value; }
}
[DefaultValue(typeof(Image), "null")]
public Image Image
{
get { return _image; }
set { _image = value; }
}
[Bindable(true)]
[Localizable(false)]
[DefaultValue("")]
[TypeConverter(typeof(StringConverter))]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Hidden)]
public object Tag
{
get { return _tag; }
set { _tag = value; }
}
#endregion
Override Methods Override Methods
public override string ToString()
{
return _text;
}
#endregion
IDisposable 成员 IDisposable 成员
public void Dispose()
{
_image = null;
_tag = null;
}
#endregion
}
实现了ListBoxExItem类对象,我们还需要实现一个ListBoxExItemCollection集合,这个集合需要实现IList、ICollection和IEnumerable接口,用来存储ListBoxExItem对象,像ListBox自己实现的ListBox.ObjectCollection集合那样,我们需要实现一些添加项、删除项等等的一些方法,看看这个集合类的视图和代码:
ListBoxExItemCollection 类视图
ListBoxExItemCollection 类源码:
[ListBindable(false)]
public class ListBoxExItemCollection
: IList,ICollection,IEnumerable
{
Fields Fields
private ListBoxEx _owner;
#endregion
Constructors Constructors
public ListBoxExItemCollection(ListBoxEx owner)
{
_owner = owner;
}
#endregion
Properties Properties
internal ListBoxEx Owner
{
get { return _owner; }
}
public ListBoxExItem this[int index]
{
get { return Owner.OldItems[index] as ListBoxExItem; }
set { Owner.OldItems[index] = value; }
}
public int Count
{
get { return Owner.OldItems.Count; }
}
public bool IsReadOnly
{
get { return Owner.OldItems.IsReadOnly; }
}
#endregion
Public Methods Public Methods
public int Add(ListBoxExItem item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
return Owner.OldItems.Add(item);
}
public void AddRange(ListBoxExItemCollection value)
{
foreach (ListBoxExItem item in value)
{
Add(item);
}
}
public void AddRange(ListBoxExItem[] items)
{
Owner.OldItems.AddRange(items);
}
public void Clear()
{
Owner.OldItems.Clear();
}
public bool Contains(ListBoxExItem item)
{
return Owner.OldItems.Contains(item);
}
public void CopyTo(
ListBoxExItem[] destination,
int arrayIndex)
{
Owner.OldItems.CopyTo(destination, arrayIndex);
}
public int IndexOf(ListBoxExItem item)
{
return Owner.OldItems.IndexOf(item);
}
public void Insert(int index, ListBoxExItem item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
Owner.OldItems.Insert(index, item);
}
public void Remove(ListBoxExItem item)
{
Owner.OldItems.Remove(item);
}
public void RemoveAt(int index)
{
Owner.OldItems.RemoveAt(index);
}
public IEnumerator GetEnumerator()
{
return Owner.OldItems.GetEnumerator();
}
#endregion
IList 成员 IList 成员
int IList.Add(object value)
{
if (!(value is ListBoxExItem))
{
throw new ArgumentException();
}
return Add(value as ListBoxExItem);
}
void IList.Clear()
{
Clear();
}
bool IList.Contains(object value)
{
return Contains(value as ListBoxExItem);
}
int IList.IndexOf(object value)
{
return IndexOf(value as ListBoxExItem);
}
void IList.Insert(int index, object value)
{
if (!(value is ListBoxExItem))
{
throw new ArgumentException();
}
Insert(index, value as ListBoxExItem);
}
bool IList.IsFixedSize
{
get { return false; }
}
bool IList.IsReadOnly
{
get { return IsReadOnly; }
}
void IList.Remove(object value)
{
Remove(value as ListBoxExItem);
}
void IList.RemoveAt(int index)
{
RemoveAt(index);
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
if (!(value is ListBoxExItem))
{
throw new ArgumentException();
}
this[index] = value as ListBoxExItem;
}
}
#endregion
ICollection 成员 ICollection 成员
void ICollection.CopyTo(Array array, int index)
{
CopyTo((ListBoxExItem[])array, index);
}
int ICollection.Count
{
get { return Count; }
}
bool ICollection.IsSynchronized
{
get { return false; }
}
object ICollection.SyncRoot
{
get { return this; }
}
#endregion
IEnumerable 成员 IEnumerable 成员
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
准备工作做好了,现在我们来动一动ListBox了,先给它加一个OldItems属性,表示的是它原来的Items属性,然后我们需要用我们自己定义的ListBoxExItemCollection集合来定义一个Items属性,覆盖原来的Items属性,这样在设计的时候就是设计我们自己定义的ListBoxExItem项了,我们就可以设置我们自己的项的图标和文本了。看看这两个属性的定义:
internal ListBox.ObjectCollection OldItems
{
get { return base.Items; }
}
[Localizable(true)]
[MergableProperty(false)]
[DesignerSerializationVisibility(
DesignerSerializationVisibility.Content)]
public new ListBoxExItemCollection Items
{
get { return _items; }
}
最后就是DrawItem了,我们直接用我们自己自定义的Items就可以获取需要绘制的ListBoxExItem项了,我们把图标和文本绘好就OK了。看看绘制的代码:
protected override void OnDrawItem(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 = new SolidBrush(backColor))
{
g.FillRectangle(brush, bounds);
}
}
Image image = item.Image;
Rectangle imageRect = new Rectangle(
bounds.X + 2,
bounds.Y + 2,
bounds.Height - 4,
bounds.Height - 4);
Rectangle textRect = new Rectangle(
imageRect.Right + 2,
bounds.Y,
bounds.Width - imageRect.Right - 2,
bounds.Height);
string text = 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程序员之窗。