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程序员之窗。

原文地址:https://www.cnblogs.com/superstar/p/1769809.html