转自微软的例子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); }