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