最近在winform应用中需要用到可分组的数据列表功能,DataGridView默认没有提供分组的功能,而OutlookGrid(http://www.codeproject.com/KB/grid/OutlookGrid.aspx)用起来又是相当的麻烦,最后发现了ObjectListView(objectlistview.sourceforge.net),功能相当的强大,强大到我不需要那么多的功能,额~~所以决定参照它的实现,对ListView做一个简单的扩展(注:本文仅针对ListView的View属性为Details)。
首先为ListView添加好列:
listViewEx1.Columns.AddRange( new ColumnHeader[] { new ColumnHeader( "列1" ), new ColumnHeader( "列2" ), new ColumnHeader( "列3" ) });
|
分组
这是ListView自带的功能,用起来也很简单,只需要把ShowGroups设置为true(默认为true),再为ListView添加ListViewGroup,并为ListViewItem设置Group即可,代码如下:
ListViewGroup g = new ListViewGroup( "分组" );
listViewEx1.Groups.Add(g); ListViewItem item = new ListViewItem( "数据项" , g);
listViewEx1.Items.Add(item); |
接下来的重绘方法,就需要通过继承ListView并重写OnDrawSubItem(DrawListViewSubItemEventArgs e)方法来实现,在重写此方法之前,必须设置ListView的OwnerDraw属性为true,用于启用重绘。
给ListViewSubItem设置图标
ListView默认可以设置ImageIndex来显示图标,但是只能设置在每个ListViewItem上,即每一行数据只能有一个图标,若要每个ListViewSubItem都能设置图标,则需要通过重写OnDrawSubItem(DrawListViewSubItemEventArgs e)方法来实现,关键代码为:
Graphics g = e.Graphics; Rectangle r = e.Bounds; Rectangle imageBounds = new Rectangle(r.Location, image.Size); //image为具体的图标文件
g.DrawImage(image, imageBounds); //通过DrawImage方法绘制图标
|
对这种方法简单的改造,可以为每一列设置单独的图标。
网格线
ListView在设置了分组的情况下,GridLines属性就无效了,所以如果需要显示网格线,也需要通过重写OnDrawSubItem(DrawListViewSubItemEventArgs e)方法来实现,关键代码为:
Graphics g = e.Graphics; Rectangle r = e.Bounds; using (Pen pen = new Pen(Color.Gray))
{ g.DrawRectangle(pen, r.X, r.Y, r.Width, r.Height + 1); //高度加1使横向线条重叠
} |
以上代码画了灰色的网格线,也可以简单改造以实现自定义的网络线颜色。
选中的背景
在重写OnDrawSubItem(DrawListViewSubItemEventArgs e)方法之后,选中ListView的行,默认的背景色不会出现,所以还得把选中状态的背景色显示出来,关键代码为:
if ((e.ItemState & ListViewItemStates.Selected)
== ListViewItemStates.Selected) {
using (SolidBrush brush = new SolidBrush(Color.Blue))
{
g.FillRectangle(brush, r);
}
}
|
以上代码为选中的列绘制了蓝色的背景,同样也可以扩展以上代码来实现自定义背景色。
呈现原始文本
在重写OnDrawSubItem(DrawListViewSubItemEventArgs e)之后,原来的文本内容同样也会像背景一样丢失了,所以需要把原来的文本重新绘制出来,关键代码如下:
Graphics g = e.Graphics; Rectangle r = e.Bounds; TextRenderer.DrawText( g,
e.SubItem.Text,
e.SubItem.Font,
r,
e.SubItem.ForeColor,
TextFormatFlags.Left);
|
其他
1、自动调整列大小:调用AutoResizeColumns方法,listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
2、隐藏列头:设置HeaderStyle属性,listView1.HeaderStyle = ColumnHeaderStyle.None
3、选择整行:设置FullRowSelect为true
4、行双击事件:绑定ListView的MouseDoubleClick事件,代码如下:
void listView1_MouseDoubleClick( object sender, MouseEventArgs e)
{ ListViewHitTestInfo info = listView1.HitTest(e.Location);
if (info != null && info.Item != null )
{
//...
}
} |
完整的代码
public class ListViewEx : ListView
{ public ListViewEx() :
base ()
{
this .OwnerDraw = true ; //用于启用重绘
}
/// <summary>
/// 图标
/// </summary>
public Image Icon { get ; set ; }
/// <summary>
/// 重绘图标
/// </summary>
public bool IsDrawIcon { get ; set ; }
/// <summary>
/// 重绘网格线
/// </summary>
public bool IsDrawGridLines { get ; set ; }
/// <summary>
/// 重绘图标列
/// </summary>
/// <param name="e"></param>
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
if (View != View.Details ||
e.ItemIndex == -1)
{
e.DrawDefault = true ;
return ;
}
Rectangle r = e.Bounds;
Graphics g = e.Graphics;
DrawSelectedBackground(e, g, r);
int paddingLeft = 0;
if (IsDrawIcon)
{
paddingLeft = this .DrawIcon(g, r, this .Icon, e.Item.BackColor).Width;
}
if (IsDrawGridLines)
{
using (Pen pen = new Pen(Color.Gray))
{
g.DrawRectangle(pen, r.X, r.Y, r.Width, r.Height + 1); //高度加1使横向线条重叠
}
}
if (! string .IsNullOrEmpty(e.SubItem.Text))
{
this .DrawText(e, g, r, paddingLeft);
}
}
/// <summary>
/// 重绘选中时背景
/// </summary>
private void DrawSelectedBackground(DrawListViewSubItemEventArgs e, Graphics g, Rectangle r)
{
if ((e.ItemState & ListViewItemStates.Selected)
== ListViewItemStates.Selected) {
using (SolidBrush brush = new SolidBrush(Color.Blue))
{
g.FillRectangle(brush, r);
}
}
}
/// <summary>
/// 重绘图标
/// </summary>
private Size DrawIcon(Graphics g, Rectangle r, Image image, Color backColor)
{
Rectangle imageBounds = new Rectangle(r.Location, image.Size);
if (image.Height > r.Height)
{
float scaleRatio = ( float )r.Height / ( float )image.Height;
imageBounds.Width = ( int )(( float )Icon.Width * scaleRatio);
imageBounds.Height = r.Height - 1;
}
//使图标不会紧贴着每一列的左上角
imageBounds.X += 1;
imageBounds.Y += 1;
g.DrawImage(image, imageBounds);
return imageBounds.Size;
}
/// <summary>
/// 重绘文本
/// </summary>
private void DrawText(DrawListViewSubItemEventArgs e, Graphics g, Rectangle r, int paddingLeft)
{
TextFormatFlags flags = GetFormatFlags(e.Header.TextAlign);
r.X += 1 + paddingLeft; //重绘图标时,文本右移
TextRenderer.DrawText(
g,
e.SubItem.Text,
e.SubItem.Font,
r,
e.SubItem.ForeColor,
flags);
}
/// <summary>
/// 获取文本对齐
/// </summary>
private TextFormatFlags GetFormatFlags(
HorizontalAlignment align)
{
TextFormatFlags flags =
TextFormatFlags.EndEllipsis |
TextFormatFlags.VerticalCenter;
switch (align)
{
case HorizontalAlignment.Center:
flags |= TextFormatFlags.HorizontalCenter;
break ;
case HorizontalAlignment.Right:
flags |= TextFormatFlags.Right;
break ;
case HorizontalAlignment.Left:
flags |= TextFormatFlags.Left;
break ;
}
return flags;
}
} |
相关的演示代码:
public Form1()
{ InitializeComponent();
ListViewEx listViewEx1 = new ListViewEx();
listViewEx1.Icon = Resources.application;
listViewEx1.IsDrawGridLines = true ;
listViewEx1.IsDrawIcon = true ;
listViewEx1.Location = new Point(0, 0);
listViewEx1.Name = "listViewEx1" ;
listViewEx1.Size = new Size(284, 265);
listViewEx1.Dock = DockStyle.Fill;
listViewEx1.FullRowSelect = true ;
listViewEx1.UseCompatibleStateImageBehavior = false ;
listViewEx1.View = View.Details;
listViewEx1.HeaderStyle = ColumnHeaderStyle.None;
listViewEx1.Columns.AddRange( new ColumnHeader[] { new ColumnHeader(), new ColumnHeader(), new ColumnHeader() });
for ( int i = 1; i <= 3; i++)
{
ListViewGroup g = new ListViewGroup( "分组" + i.ToString());
listViewEx1.Groups.Add(g);
for ( int j = 1; j <= 5; j++)
{
ListViewItem item = new ListViewItem(j.ToString(), g);
item.SubItems.Add((i + j).ToString());
item.SubItems.Add((i * j).ToString());
listViewEx1.Items.Add(item);
}
}
this .Controls.Add(listViewEx1);
} |