转自微软的例子
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using System.Data;
using System.Reflection;
using System.Drawing.Imaging;
namespace SmartUI {
enum MailIcon {
unopened,
opened
}
/// <summary>
/// Class used to store display score info
/// </summary>
class MailItem {
public MailIcon icon;
public string sender;
public string subject;
public MailItem(MailIcon Icon, string Sender, string Subject)
{
this.icon = Icon;
this.sender = Sender;
this.subject = Subject;
}
}
/// <summary>
/// Provide a 2 line listview
/// </summary>
class MyListView : OwnerDrawnList {
constint Column1Left = 5;//条目位图X位置
const int Column2Left = 25;//条目文字X位置
public MyListView() {
// We need a total of 5 rows, so the height is 180/5=36
Graphics g = this.CreateGraphics();
Font font = new Font(this.Font.Name, 10, FontStyle.Bold);
// Calc line height to be height of letter A plus 10%
int fontHeight = (int)(g.MeasureString("A", font).Height*1.1);//计算当前字体的高度*1.1作为字体高度
this.ItemHeight = fontHeight*2; //每一条目为两行汉字,因此为字体高度*2
g.Dispose();
}
//键盘处理流程。上/下键已由基类处理,这里只处理回车键
protected override void OnKeyDown(KeyEventArgs e) {
switch(e.KeyCode) {
case Keys.Return:
MessageBox.Show("You selected item " + this.SelectedIndex.ToString(),
"Item selected",
MessageBoxButtons.OK,
MessageBoxIcon.Asterisk,
MessageBoxDefaultButton.Button1);
break;
}
base.OnKeyDown(e);
}
/// <summary>
/// 自定义绘图事件。绘制ListView的条目
/// </summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e) {
// Declare vars
Font font;
Color fontColor;
string bmpName;
// 从控件位图中产生控件绘图区,注:控件位图为控件不含滚动条的可显示区域图像,即条目显示部分
Graphics gOffScreen = Graphics.FromImage(this.OffScreen);
//以控件背景色填充绘图区
gOffScreen.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
int itemTop = 0;//当前条目的Y坐标
// 绘制可显示的条目,从滚动条当前位置绘制DrawCount个条目
for(int n = this.VScrollBar.Value; n < this.VScrollBar.Value + DrawCount; n++)
{
//如果该条目为当前选中条目,则要高亮显示
if(n == this.SelectedIndex)
{
//用高亮色填出该条目区域
gOffScreen.FillRectangle(new SolidBrush(SystemColors.Highlight),
0,
itemTop,
//如果滚动条可显,则要减去滚动条的宽度
this.ClientSize.Width - (this.VScrollBar.Visible ? this.VScrollBar.Width : 0),
this.ItemHeight);
//字体颜色为高亮色的对比色
fontColor = CalcTextColor(SystemColors.Highlight);
}
else
fontColor = this.ForeColor; //否则,字体颜色为控件前景色
// 用灰色绘制分割线
gOffScreen.DrawLine(new Pen(Color.DarkGray),
1,
itemTop+this.ItemHeight,
this.ClientSize.Width - (this.VScrollBar.Visible ? this.VScrollBar.Width : 2),
itemTop+this.ItemHeight);
// 取得当前条目
MailItem lvi = (MailItem)this.Items[n];
// 设置字体和条目位图
if (lvi.icon == MailIcon.unopened) { //如果为未开状态,则设置为粗体字
font = new Font(this.Font.Name, 10, FontStyle.Bold);
bmpName = "SmartUI.unread.bmp";//位图的名称
}
else {
font = new Font(this.Font.Name, 10, FontStyle.Regular);
bmpName = "SmartUI.read.bmp";
}
// Load image
Bitmap bmp = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream(bmpName));
//设置图片的透明颜色,在本实例中为红色
ImageAttributes ia = new ImageAttributes();
ia.SetColorKey(Color.Red, Color.Red);
//设置位图绘图区域
Rectangle imgRect = new Rectangle(Column1Left, itemTop+3, bmp.Width, bmp.Height);
// 绘制条目位图
gOffScreen.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia);
// 绘制条目文字
gOffScreen.DrawString(lvi.sender, font, new SolidBrush(fontColor), Column2Left, itemTop);
// 绘制条目文字
gOffScreen.DrawString(lvi.subject, font, new SolidBrush(fontColor), Column2Left, itemTop + (ItemHeight/2));
// 清理
font.Dispose();
bmp.Dispose();
// 设置下一个条目的Y位置=当前条目的Y位置+条目高度
itemTop += this.ItemHeight;
}
// 将整个绘图位图绘制到控件上
e.Graphics.DrawImage(this.OffScreen, 0, 0);
gOffScreen.Dispose();
}
// Draws the external border around the control.
protected override void OnPaintBackground(PaintEventArgs e) {
// e.Graphics.DrawRectangle(new Pen(Color.Black), 0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
}
}
// 基类 OwnerDrawnList
class OwnerDrawnList : Control {
int scrollWidth;
int itemHeight = -1;
int selectedIndex = -1;
Bitmap offScreen; //控件位图为控件不含滚动条的可显示区域图像,即条目显示部分
VScrollBar vs; //竖向滚动条
ArrayList items;//条目数组
public OwnerDrawnList() {
this.vs = new VScrollBar(); //产生滚动条
scrollWidth = this.vs.Width; //设定滚动条宽度=控件默认宽度
this.vs.Parent = this; //滚动条的父窗口为控件
this.vs.Visible = false; //不可见
this.vs.SmallChange = 1; //最小变化量为1
this.vs.ValueChanged += new EventHandler(this.ScrollValueChanged);//设置滚动变化事件
// Items to draw
this.items = new ArrayList();//产生Item数组
}
/// <summary>
/// 条目数组
/// </summary>
public ArrayList Items {
get { return this.items;}
}
/// <summary>
/// 控件位图
/// </summary>
protected Bitmap OffScreen {
get {return this.offScreen;}
}
/// <summary>
/// 控件滚动条
/// </summary>
protected VScrollBar VScrollBar {
get {return this.vs;}
}
/// <summary>
/// 选中条目索引改变事件
/// </summary>
public event EventHandler SelectedIndexChanged;
// Raise the SelectedIndexChanged event
protected virtual void OnSelectedIndexChanged(EventArgs e) {
if(this.SelectedIndexChanged != null) //如果设置了选中Item变化的事件,则执行
this.SelectedIndexChanged(this, e);
}
// 获取或设置当前选中项目索引
public int SelectedIndex {
get {return this.selectedIndex;} //返回索引
set {
this.selectedIndex = value; //设置新索引值,并调用索引改变事件
if (this.SelectedIndexChanged != null)
this.SelectedIndexChanged(this, EventArgs.Empty);
}
}
protected void ScrollValueChanged(object o, EventArgs e) {
this.Refresh();
}
protected virtual int ItemHeight {
get {return this.itemHeight;}
set {this.itemHeight = value;}
}
// If the requested index is before the first visible index then set the
// first item to be the requested index. If it is after the last visible
// index, then set the last visible index to be the requested index.
public void EnsureVisible(int index) {//计算滚动条的当前位置
if(index < this.vs.Value) {//如果新位置<原有滚动条位置
this.vs.Value = index;
this.Refresh();
}
else if(index >= this.vs.Value + this.DrawCount) {
this.vs.Value = index - this.DrawCount + 1;
this.Refresh();
}
}
// 键盘事件,处理上下键
protected override void OnKeyDown(KeyEventArgs e) {
switch(e.KeyCode) {
case Keys.Down: //Down键时,如果当前选中索引<滚动条的最大值,则当前选中索引+1,并显示
if(this.SelectedIndex < this.vs.Maximum) {
EnsureVisible(++this.SelectedIndex);
this.Refresh();
}
break;
case Keys.Up://Up键时,如果当前选中索引>滚动条的最小值,则当前选中索引-1,并显示
if(this.SelectedIndex > this.vs.Minimum) {
EnsureVisible(--this.SelectedIndex);
this.Refresh();
}
break;
}
base.OnKeyDown(e); //调用基类的键盘事件
}
//计算实际需要绘制条目数量
protected int DrawCount {
get {
if(this.vs.Value + this.vs.LargeChange > this.vs.Maximum)//如果从当前位置到最尾+可显示最大栏目数量>总栏目数量
return this.vs.Maximum - this.vs.Value + 1; //返回实际栏目数量
else
return this.vs.LargeChange; //否则,返回可显示最大栏目数量
}
}
//控件尺寸调整事件
protected override void OnResize(EventArgs e) {
int viewableItemCount = this.ClientSize.Height / this.ItemHeight; //可显示栏目数量=控件高度/条目高度
this.vs.Bounds = new Rectangle(this.ClientSize.Width - scrollWidth,//重新调整滚动条大小
0,
scrollWidth,
this.ClientSize.Height);
//重新计算控件位图的大小
//判断是否需要滚动条
if(this.items.Count > viewableItemCount) { //如果条目数大于可显示条目数,则显示滚动条,并把滚动条最大变化量设置为可显示条目数
this.vs.Visible = true;
this.vs.LargeChange = viewableItemCount;
//产生一个控件可显示区位图
//宽度=控件宽度-滚动条宽度
//高度=控件高度
this.offScreen = new Bitmap(this.ClientSize.Width - scrollWidth, this.ClientSize.Height);
}
else { //如果不需要滚动条
this.vs.Visible = false;
this.vs.LargeChange = this.items.Count;
//产生一个控件可显示区位图
this.offScreen = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
}
this.vs.Maximum = this.items.Count - 1; //计算滚动条的最大值=条目个数-1
}
//决定什么颜色作为高亮选中条目的字体颜色
protected Color CalcTextColor(Color backgroundColor) {
if(backgroundColor.Equals(Color.Empty))
return Color.Black; //如果背景颜色为空,则返回黑色
int sum = backgroundColor.R + backgroundColor.G + backgroundColor.B;
if(sum > 256) //如果红绿蓝颜色大于256,则返回黑色
return Color.Black;
else
return Color.White; //否则返回白色
}
}
}
----------------------------------------------------------------------------------------------------------------------------------------------
调用方法
public CustomListView()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
olv = new MyListView();
olv.Parent = this;
// Set the bounds of the listview.
olv.Bounds = new Rectangle(0,0, this.ClientSize.Width, this.ClientSize.Height);
// Add data items to listview
for (int i=0; i<10; i++) {
olv.Items.Add(new MailItem(MailIcon.unopened, "James Pratt", "re: Custom ListView"));
olv.Items.Add(new MailItem(MailIcon.opened, "Chung Webster", "Custom ListView"));
}
olv.SelectedIndex = 0;
olv.EnsureVisible(olv.SelectedIndex);
}