小Q的博客

  • 首页
  • net编程
    • 产品和框架
    • 开发实例
    • 经验技巧
    • 开源组件
  • wp独立站
  • 自媒体
  • 日记本
  • 工具箱
每个程序员,都应该有一个自己的博客站
  1. 首页
  2. net编程
  3. 经验技巧
  4. 正文

net中winform教程 解决ListView控件加载数据慢

2023年4月19日 122点热度 0人点赞 0条评论

Winform中的ListView控件,其实应用很广泛,资源管理器(我的电脑)就是用的ListView控件。查看方式有很多种,包括大小图标、详细信息等,用来展示数据非常不错。最近用它做项目的时候,遇到个问题,就是加载很多数据的时候非常慢,如果不使用多线程+异步,程序就要卡一段时间。经过一系列的测试,问题还是能解决的,下面用2种方法解决。

介绍方法之前,先看下测试代码,假设有10个组,每个有200个项,每项带固定图标。最原始的方法,一般需要27-30秒之间。这是肯定不能接受的。

DataTable dtItem = new DataTable();
dtItem.Columns.Add("FileName", typeof(string));
dtItem.Columns.Add("FullName", typeof(string));
dtItem.Columns.Add("ImageKey", typeof(string));
dtItem.Columns.Add("GroupName", typeof(string));

for (int i = 1; i <= 10; i++)
{
    string groupName = "group" + i.ToString();

    for (int j = 1; j <= 200; j++)
    {
        string file = "file-" + j.ToString();
        dtItem.Rows.Add(new object[] { groupName + "-" + file, file, "nine_file_mp4", groupName });
    }
}

//最原始方法,耗时:29383毫秒、27320毫秒
FormUIHelper.SetListViewDataSourceEx(this.listview1, dtItem, "FileName", "FileName", "GroupName");

 

目录

  • 方法1:BeginUpdate和EndUpdate
  • 方法2:VirtualMode虚方法模式

方法1:BeginUpdate和EndUpdate

看这2个方法命名就知道是什么意思,加载ListViewItem项前后使用,相当于告诉ListView控件,我要开始加载数据了。经测试,加载耗时10-12秒。提升60%左右了。

//方法2,耗时:12917毫秒、11815毫秒
this.listview1.BeginUpdate();
FormUIHelper.SetListViewDataSourceEx(this.listview1, dtItem, "FileName", "FileName", "GroupName");
this.listview1.EndUpdate();

 

方法2:VirtualMode虚方法模式

不得不说,这个方法真是快。同样的数据,加载是几百毫秒(即不到1秒)的时候。这性能提升效果,扛扛的。不过,这个小问题,用这种方式如何实现分组方式?

//方法3,耗时:几百毫秒级
this.myCache = new List<ListViewItem>();
foreach(DataRow drItem in dtItem.Rows)
{
    string priValue = drItem["FileName"] as string;
    string dispValue = drItem["FullName"] as string;
    string group = drItem["GroupName"] as string;

    List<string> listArgs = new List<string>();
    listArgs.Add(group + "-" + priValue);
    listArgs.Add(dispValue);

    //判断是否存在ImageKey栏目.若存在,则设置图标
    string imageKey = null;
    if (dtItem.Columns.Contains("ImageKey"))
    {
        imageKey = drItem["ImageKey"] as string;
    }

    //注:虚模式下,ImageKey不可用,要用ImageIndex
    int imageIndex = FormUIHelper.GetIndexInImageList(this.imageList3, imageKey);

    //ListViewGroup groupAdd = dicGroup[group];
    //ListViewItem lviAdd = new ListViewItem(listArgs.ToArray(), imageKey, groupAdd);
    ListViewItem lviAdd = new ListViewItem(listArgs.ToArray(), imageKey);
    lviAdd.Tag = drItem;

    lviAdd.Name = group + "-" + priValue;
    lviAdd.Text = group + "-" + dispValue;
    lviAdd.ImageKey = imageKey;
    lviAdd.ImageIndex = imageIndex;

    this.myCache.Add(lviAdd);
}

this.listview1.ShowGroups = true;
this.listview1.VirtualMode = true;
this.listview1.VirtualListSize = this.myCache.Count;
this.listview1.RetrieveVirtualItem += listview1_RetrieveVirtualItem;

 

这个事件也要写代码,不然加载失败

private void listview1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    if (this.myCache != null)
    {
        e.Item = this.myCache[e.ItemIndex];
    }
    else
    {
        //A cache miss, so create a new ListViewItem and pass it back.
        int x = e.ItemIndex * e.ItemIndex;
        e.Item = new ListViewItem(x.ToString());
    }
}

 

还有一种方法,应该也不错,就是先生成好ListViewItem的列表,然后调用this.listview1.Items.AddRange()方法,效果应该也不差。但肯定没有方法2好。

 

附SetListViewDataSourceEx方法

/// <summary>
/// 加载ListView控件数据源,根据DataTable对象
/// 注:支持分组
/// </summary>
/// <param name="listView"></param>
/// <param name="dataTable"></param>
/// <param name="primaryKey">主键名称</param>
/// <param name="displayColumn">显示列名称</param>
/// <param name="groupName">分组名称</param>
/// <param name="dicColumn">Details时的Column列表</param>
public static void SetListViewDataSourceEx(ListView listView, DataTable dataTable, string primaryKey, string displayColumn, string groupName, Dictionary<string, int> dicColumn = null)
{
    //参数检测
    if (listView == null) return;
    if (dataTable == null) return;

    //先增加列            
    if (dicColumn == null)
    {
        listView.Columns.Clear();
    }
    else
    {
        listView.Columns.Clear();

        foreach (string col in dicColumn.Keys)
        {
            int colWidth = dicColumn[col];
            listView.Columns.Add(col, colWidth);
        }
    }

    //判断是否需要分组?
    if (string.IsNullOrEmpty(groupName))
    {
        listView.Items.Clear();

        foreach (DataRow drData in dataTable.Rows)
        {
            object primaryValue = drData[primaryKey];
            if (StringHelper.ObjectIsNullOrEmpty(primaryValue)) continue;

            object displayColumnValue = drData[displayColumn];
            if (StringHelper.ObjectIsNullOrEmpty(displayColumnValue))
                displayColumnValue = "";


            //判断是否栏目值存在?
            ListViewItem[] lviFinds = listView.Items.Find(primaryValue.ToString(), false);
            if (lviFinds != null && lviFinds.Length > 0) continue;

            List<string> listArgs = new List<string>();
            foreach (DataColumn dc in dataTable.Columns)
            {
                object columnValue = drData[dc];
                if (StringHelper.ObjectIsNullOrEmpty(columnValue))
                    columnValue = "";

                listArgs.Add(columnValue.ToString());
            }

            //判断是否存在ImageKey栏目.若存在,则设置图标
            string imageKey = null;
            if (dataTable.Columns.Contains("ImageKey"))
            {
                imageKey = drData["ImageKey"] as string;
            }

            ListViewItem lviAdd = null;
            if (string.IsNullOrEmpty(imageKey))
            {
                lviAdd = new ListViewItem(listArgs.ToArray());
            }
            else
            {
                lviAdd = new ListViewItem(listArgs.ToArray(), imageKey);
            }
            lviAdd.Tag = drData;

            lviAdd.Name = primaryValue.ToString();
            lviAdd.Text = displayColumnValue.ToString();
            listView.Items.Add(lviAdd);
        }
    }
    else
    {
        listView.ShowGroups = true;
        listView.Groups.Clear();
        listView.Items.Clear();

        DataTable dtDistinct = DataTableHelper.GetTableByDistinct(dataTable, new string[] { groupName });
        foreach (DataRow drDistinct in dtDistinct.Rows)
        {
            object value = drDistinct[0];
            if (StringHelper.ObjectIsNullOrEmpty(value)) continue;

            //增加分组对象
            ListViewGroup groupAdd = new ListViewGroup(value.ToString());
            listView.Groups.Add(groupAdd);

            DataRow[] drFinds = dataTable.Select(groupName + " = '" + value.ToString() + "'");
            foreach (DataRow drFind in drFinds)
            {
                object primaryValue = drFind[primaryKey];
                if (StringHelper.ObjectIsNullOrEmpty(primaryValue)) continue;

                object displayColumnValue = drFind[displayColumn];
                if (StringHelper.ObjectIsNullOrEmpty(displayColumnValue))
                    displayColumnValue = "";

                //判断是否栏目值存在?
                ListViewItem[] lviFinds = listView.Items.Find(primaryValue.ToString(), false);
                if (lviFinds != null && lviFinds.Length > 0) continue;

                List<string> listArgs = new List<string>();
                foreach (DataColumn dc in dataTable.Columns)
                {
                    object columnValue = drFind[dc];
                    if (StringHelper.ObjectIsNullOrEmpty(columnValue)) columnValue = "";
                    listArgs.Add(columnValue.ToString());
                }

                //判断是否存在ImageKey栏目.若存在,则设置图标
                string imageKey = null;
                if (dataTable.Columns.Contains("ImageKey"))
                {
                    imageKey = drFind["ImageKey"] as string;
                }

                ListViewItem lviAdd = null;
                if (string.IsNullOrEmpty(imageKey))
                {
                    lviAdd = new ListViewItem(listArgs.ToArray(), groupAdd);
                }
                else
                {
                    lviAdd = new ListViewItem(listArgs.ToArray(), imageKey, groupAdd);
                }
                lviAdd.Tag = drFind;

                lviAdd.Name = primaryValue.ToString();
                lviAdd.Text = displayColumnValue.ToString();
                listView.Items.Add(lviAdd);
            }
        }
    }
}

 

 

相关阅读

net中winform教程 ListView控件如何实现分组?

标签: ListView 加载大数据慢 虚方法
最后更新:2023年4月19日

小Q

80后中年不油腻大叔,喜欢编写代码、打羽毛球、做木制玩具。目前定居浙江杭州

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022 小Q的博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

浙ICP备2022019157号-2