【分享】C# 重绘ListView(列表头不包含列表项的部分也实现了重绘)

starts_2000 2009-08-18 09:27:45
原文含下载
看到论坛上很多人问到怎么把ListView控件的列表头不包含列表项的最后那一部分也进行重绘,看起来更美观一些,但是基本都没能解决,今天就发这篇文章,提供一总解决方法吧。具体文章看 C# WinForm控件美化扩展系列之ListView
ListView其实由两部分组成的,它包含了一个Header部分,用SPY++看看就知道了,要实现对列表最后一部分的美化,直接重写ListView的WndProc方法,截取WM_PAINT或者WM_NCPAINT消息都是不能对他进行很好的处理的,我们需要截取这个Header的消息才行,这点是至关重要的。要怎么截取他的消息,其实前面实有一篇文章C# 实现只能输入数字的ComboBox控件已经用到过这种方法了,以后写的控件可能会经常看到这种方法。第一步就是得到Header的句柄;第二步,继承NativeWindow,实现一个HeaderNativeWindow类,把Header的句柄分配给他。第三步,在HeaderNativeWindow类中重写NativeWindow的WndProc方法,然后进行相应的消息处理。在这里,我对WM_PAINT(0xF)进行了处理,在这个消息中进行了重绘。但是,我测试的时候发现,当改变ListView的大小的时候,变大没问题,还是正常的绘制,但是变小的时候,Header没有收到WM_PAINT消息,所以就没有重绘,但是可以收到WM_WINDOWPOSCHANGED(0x47)消息,所以我就在收到WM_WINDOWPOSCHANGED消息的时候也进行了重绘。第四步,当ListView创建句柄的时候,创建一个HeaderNativeWindow,让它可以截取Header的消息。当ListView销毁句柄时,HeaderNativeWindow也要释放Header的句柄。

方法有了,但是在重绘的时候我们需要知道需要绘制部分的大小和位置,看起来很简单,就是用Header的宽度减去最后一个列表头的最右边所在的位置,就得到要绘制部分的宽度了,高度就是列表头的高度,但是实现起来还是有点麻烦的。先发送一个HDM_GETITEMRECT到Header,获取最右边的列表头的位置和大小,然后再获得Header的大小,这样就可以计算出需要绘制部分的大小和位置了。看看代码:
private Rectangle HeaderEndRect()

{

RECT rect = new RECT();

IntPtr headerWnd = HeaderWnd;

SendMessage(

headerWnd, HDM_GETITEMRECT, ColumnAtIndex(ColumnCount - 1), ref rect);

int left = rect.Right;

GetWindowRect(headerWnd, ref rect);

OffsetRect(ref rect, -rect.Left, -rect.Top);

rect.Left = left;



return Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);

}

现在再来看看HeaderNativeWindow类的完整代码:
private class HeaderNativeWindow : NativeWindow,IDisposable

{

private ListViewEx _owner;



public HeaderNativeWindow(ListViewEx owner)

: base()

{

_owner = owner;

base.AssignHandle(owner.HeaderWnd);

}



protected override void WndProc(ref Message m)

{

base.WndProc(ref m);

if (m.Msg == 0xF || m.Msg == 0x47)

{

IntPtr hdc = GetDC(m.HWnd);

try

{

using (Graphics g = Graphics.FromHdc(hdc))

{

Rectangle bounds = _owner.HeaderEndRect();

Color baseColor = _owner.HeadColor;

Color borderColor = _owner.HeadColor;

Color innerBorderColor = Color.FromArgb(200, 255, 255, 255);



_owner.RenderBackgroundInternal(

g,

bounds,

baseColor,

borderColor,

innerBorderColor,

0.45f,

true,

LinearGradientMode.Vertical);

}

}

finally

{

ReleaseDC(m.HWnd, hdc);

}

}

}



#region IDisposable 成员



public void Dispose()

{

ReleaseHandle();

_owner = null;

}



#endregion

}

再来看看对ListView创建和销毁句柄的两个方法OnHandleCreated和OnHandleDestroyed的重写。

protected override void OnHandleCreated(EventArgs e)

{

base.OnHandleCreated(e);

if (_headerNativeWindow == null)

{

if (HeaderWnd != IntPtr.Zero)

{

_headerNativeWindow = new HeaderNativeWindow(this);

}

}

}



protected override void OnHandleDestroyed(EventArgs e)

{

base.OnHandleDestroyed(e);

if (_headerNativeWindow != null)

{

_headerNativeWindow.Dispose();

_headerNativeWindow = null;

}

}
最后来看看效果:
...全文
1283 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
rzj2009 2009-08-28
  • 打赏
  • 举报
回复
支持原创,大家顶
rzj2009 2009-08-27
  • 打赏
  • 举报
回复
顶就一个字
24K純帥 2009-08-25
  • 打赏
  • 举报
回复
不错,支持~
lixb 2009-08-25
  • 打赏
  • 举报
回复
rzj2009 2009-08-21
  • 打赏
  • 举报
回复
很漂亮
BATTLERxANGE 2009-08-20
  • 打赏
  • 举报
回复
好好好,美化窗体的,把窗体弄漂亮的,我全收了,哈哈哈哈,越漂亮越好
rzj2009 2009-08-20
  • 打赏
  • 举报
回复
看了又看
rzj2009 2009-08-20
  • 打赏
  • 举报
回复
感谢楼主
lixb 2009-08-19
  • 打赏
  • 举报
回复
顶就一个字
wuyq11 2009-08-19
  • 打赏
  • 举报
回复
不错,支持
zjh222 2009-08-19
  • 打赏
  • 举报
回复
我需要是的把标头加高,可是这个只是绘制原来的标头高度..
也没有发现有把标头加高的属性呢
hai_yang_09 2009-08-18
  • 打赏
  • 举报
回复
jf
dylike 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 starts_2000 的回复:]
引用 2 楼 dylike 的回复:
顶,感谢分享.
悄悄说一句:"你美化的没有我的好看哦" :)

美化我只是随便做了下,我提供的只是实现的方法,要画更好看的直接自己画就OK,
看了你的大小,很多都不开源的
[/Quote]

呵呵.下午我就开源吧.
starts_2000 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 dylike 的回复:]
顶,感谢分享.
悄悄说一句:"你美化的没有我的好看哦" :)
[/Quote]
美化我只是随便做了下,我提供的只是实现的方法,要画更好看的直接自己画就OK,
看了你的大小,很多都不开源的
tristan0429 2009-08-18
  • 打赏
  • 举报
回复
不错,jf
dylike 2009-08-18
  • 打赏
  • 举报
回复
顶,感谢分享.
悄悄说一句:"你美化的没有我的好看哦" :)
编程有钱人了 2009-08-18
  • 打赏
  • 举报
回复
jf
robin521 2009-08-18
  • 打赏
  • 举报
回复
再弱弱的说一句,代码我看不懂@
robin521 2009-08-18
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 dylike 的回复:]
顶,感谢分享.
悄悄说一句:"你美化的没有我的好看哦" :)
[/Quote]

又看到你了,好吧,你做的确实比他好.
没法下载,到这里折腾一把试试。 本文由abc2253130贡献 doc文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。 C#(WINFORM)学习 一、 C#基础 基础 类型和变量 类型和变量 类型 C# 支持两种类型:“值类型”和“引用类型”。值类型包括简单类型(如 char、int 和 float 等)、枚举类型和结构类型。引用类型包括类 (Class)类 型、接口类型、委托类型和数组类型。 变量的类型声明 变量的类型声明 每个变量必须预先声明其类型。如 int a; int b = 100; float j = 4.5; string s1; 用 object 可以表示所有的类型。 预定义类型 下表列出了预定义类型,并说明如何使用。 类型 object 说明 所有其他类型的最终 基类型 字符串类型; 字符串是 Unicode 字符序列 8 位有符号整型 16 位有符号整型 32 位有符号整型 64 位有符号整型 示例 object o = null; 范围 string sbyte short int long string s = "hello"; sbyte val = 12; short val = 12; int val = 12; long val1 = 12; -128 到 127 -32,768 到 32,767 -2,147,483,648 2,147,483,647 -9,223,372,036,854,775,808 到 第1页 C#(WINFORM)学习 long val2 = 34L; 到 9,223,372,036,854,775,807 byte ushort 8 位无符号整型 16 位无符号整型 byte val1 = 12; ushort val1 = 12; uint val1 = 12; uint 32 位无符号整型 uint val2 = 34U; ulong val1 = 12; ulong val2 = 34U; ulong 64 位无符号整型 ulong val3 = 56L; ulong val4 = 78UL; float 单精度浮点型 float val = 1.23F;7 位 double val1 = 1.23; double 双精度浮点型 double val2 = ±5.0 × 10?324 ±1.7 × 10 308 0 到 255 0 到 65,535 0 到 4,294,967,295 0 到 18,446,744,073,709,551,615 ±1.5 × 10?45 ±3.4 × 10 38 到 到 4.56D;15-16 布尔型;bool 值或为 真或为假 字符类型;char 值是 一个 Unicode 字符 精确的小数类型, 具有 28 个有效数字 bool val1 = true; bool val2 = false; char val = 'h'; decimal val = bool char decimal DateTime ±1.0 × 10?28 ±7.9 × 10 28 到 1.23M;28-29 变量转换 简单转换: float f = 100.1234f; 可以用括号转换: short s = (short)f 也可以利用 Convert 方法来转换: string s1; s1=Convert.ToString(a); MessageBox.Show(s1); 常用 Convert 方法有: 第2页 C#(WINFORM)学习 C# Convert.ToBoolean Convert.ToByte Convert.ToChar Convert.ToDateTime Convert.ToDecimal Convert.ToDouble Convert.ToInt16 Convert.ToInt32 Convert.ToInt64 Convert.ToSByte Convert.ToSingle Convert.ToString Convert.ToUInt16 Convert.ToUInt32 Convert.ToUInt64 备注 Math 类 常用科学计算方法: C# Math.Abs Math.Sqrt Math.Ro

110,535

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧