• winform ListView应用之分组、重绘图标、网格线


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

    DEMO下载

    作者:囧月
    出处:http://lwme.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    hiho#1445 重复旋律5 求子串数量 后缀自动机
    SPOJ LCS2 后缀自动机
    SPOJ-LCS 后缀自动机
    bzoj 3261 最大异或和 可持久化字典树(01树)
    【洛谷1297】单选错位
    【HAOI2008】木棍分割
    【SDOI2016】排列计数
    【HAOI2008】下落的圆盘
    【HAOI2008】硬币购物
    【洛谷5520】青原樱
  • 原文地址:https://www.cnblogs.com/lwme/p/1911734.html
Copyright © 2020-2023  润新知