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

    技术2022-05-12  29

            看到论坛上很多人问到怎么把ListView控件的列表头不包含列表项的最后那一部分也进行重绘,看起来更美观一些,但是基本都没能解决,今天就发这篇文章,提供一种解决方法吧。具体文章看 C# WinForm控件美化扩展系列之ListView。

           

    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


    最新回复(0)