C# WinForm控件美化扩展系列之ListView(2)

  前面的一篇文章C# WinForm控件美化扩展系列之ListView实现了隔行不同颜色和对列表头进行了美化,但遗憾的是对列表的最后的不包含列头的部分没有进行重绘,主要原因是上次没时间处理,因为处理那一部分是比较困难的,需要花不少时间,今天总算有时间补偿上次的遗憾了。

网上对列表控件(ListView)的美化也很少有对不包含列表部分的重绘,C#的就更加少了,但是很多人都在各个论坛问到这个东西,也少有人回答,主要是这个用到API方面的东西比较多,也不是太好处理,今天把这篇文章写出来,也算是弥补一下这方面的资料吧,也是告诉大家,其他语言能实现的C#也都可以实现,只要找对方法就行了,好了,废话不多说了,进入正题。

ListView其实由两部分组成的,它包含了一个Header部分,用SPY++看看就知道了,要实现对列表最后一部分的美化,直接重写ListViewWndProc方法,截取WM_PAINT或者WM_NCPAINT消息都是不能对他进行很好的处理的,我们需要截取这个Header的消息才行,这点是至关重要的。要怎么截取他的消息,其实前面实有一篇文章C# 实现只能输入数字的ComboBox控件已经用到过这种方法了,以后写的控件可能会经常看到这种方法。第一步就是得到Header的句柄;第二步,继承NativeWindow,实现一个HeaderNativeWindow类,把Header的句柄分配给他。第三步,在HeaderNativeWindow类中重写NativeWindowWndProc方法,然后进行相应的消息处理。在这里,我对WM_PAINT0xF)进行了处理,在这个消息中进行了重绘。但是,我测试的时候发现,当改变ListView的大小的时候,变大没问题,还是正常的绘制,但是变小的时候,Header没有收到WM_PAINT消息,所以就没有重绘,但是可以收到WM_WINDOWPOSCHANGED0x47)消息,所以我就在收到WM_WINDOWPOSCHANGED消息的时候也进行了重绘。第四步,当ListView创建句柄的时候,创建一个HeaderNativeWindow,让它可以截取Header的消息。当ListView销毁句柄时,HeaderNativeWindow也要释放Header的句柄。

方法有了,但是在重绘的时候我们需要知道需要绘制部分的大小和位置,看起来很简单,就是用Header的宽度减去最后一个列表头的最右边所在的位置,就得到要绘制部分的宽度了,高度就是列表头的高度,但是实现起来还是有点麻烦的。先发送一个HDM_GETITEMRECTHeader,获取最右边的列表头的位置和大小,然后再获得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创建和销毁句柄的两个方法OnHandleCreatedOnHandleDestroyed的重写。

        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;

            }

        }

最后来看看效果:

声明:

本文版权归作者和CS 程序员之窗所有,欢迎转载,转载必须保留以下版权信息,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

作者:Starts_2000

出处:CS 程序员之窗 http://www.csharpwin.com

你可以免费使用或修改提供的源代码,但请保留源代码中的版权信息,详情请查看:

CS程序员之窗开源协议 http://www.csharpwin.com/csol.html

你可能感兴趣的:(winform,控件)