最近在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);
}
|