.NET Listview刷新

时间:2010-07-21 12:13:19

标签: c# .net winforms listview

我有以下代码,它基本上从数据库获取值并填充listview。

using (IDataReader reader = cmd.ExecuteReader())
{                    
    lvwMyList.Items.Clear();
    while (reader.Read())
    {
        ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
        lvi.SubItems.Add(reader["Value2"].ToString());                    
    }
}

我遇到的问题是,它会以较短的间隔(每秒)重复执行,导致列表视图中的项目不断消失并重新出现。有没有办法阻止列表视图刷新,直到完成更新?如下所示:

using (IDataReader reader = cmd.ExecuteReader())
{                    
    lvwMyList.Items.Freeze(); // Stop the listview updating
    lvwMyList.Items.Clear();
    while (reader.Read())
    {
        ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
        lvi.SubItems.Add(reader["Value2"].ToString());                    
    }
    lvwMyList.Items.UnFreeze(); // Refresh the listview
}

3 个答案:

答案 0 :(得分:9)

像这样:

try
{
    lvwMyList.BeginUpdate();
    //bla bla bla

}
finally
{
    lvwMyList.EndUpdate();
}

如果您想在填写前清除列表,请务必在 lvwMyList.Items.Clear()之后调用BeginUpdate

答案 1 :(得分:1)

这是我第一次在StackOverflow上发帖,所以请原谅下面的凌乱代码格式。

为了防止在更新ListView时锁定表单,您可以使用我编写的以下方法来解决此问题。

注意:如果您希望使用超过20,000个项目填充ListView,则不应使用此方法。如果需要向ListView添加超过20k的项目,请考虑在虚拟模式下运行ListView。

 public static async void PopulateListView<T>(ListView listView, Func<T, ListViewItem> func, 
        IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
    {
        if (listView != null && listView.IsHandleCreated)
        {
            var conQue = new ConcurrentQueue<ListViewItem>();

            // Clear the list view and refresh it
            if (listView.InvokeRequired)
            {
                listView.BeginInvoke(new MethodInvoker(() =>
                    {
                        listView.BeginUpdate();
                        listView.Items.Clear();
                        listView.Refresh();
                        listView.EndUpdate();
                    }));
            }
            else
            {
                listView.BeginUpdate();
                listView.Items.Clear();
                listView.Refresh();
                listView.EndUpdate();
            }

            // Loop over the objects and call the function to generate the list view items
            if (objects != null)
            {
                int objTotalCount = objects.Count();

                foreach (T obj in objects)
                {
                    await Task.Run(() =>
                        {
                            ListViewItem item = func.Invoke(obj);

                            if (item != null)
                                conQue.Enqueue(item);

                            if (progress != null)
                            {
                                double dProgress = ((double)conQue.Count / objTotalCount) * 100.0;

                                if(dProgress > 0)
                                    progress.Report(dProgress > int.MaxValue ? int.MaxValue : (int)dProgress);
                            }
                        });
                }

                // Perform a mass-add of all the list view items we created
                if (listView.InvokeRequired)
                {
                    listView.BeginInvoke(new MethodInvoker(() =>
                        {
                            listView.BeginUpdate();
                            listView.Items.AddRange(conQue.ToArray());
                            listView.Sort();
                            listView.EndUpdate();
                        }));
                }
                else
                {
                    listView.BeginUpdate();
                    listView.Items.AddRange(conQue.ToArray());
                    listView.Sort();
                    listView.EndUpdate();
                }
            }
        }

        if (progress != null)
            progress.Report(100);
    }

您不必提供IProgress对象,只需使用null,该方法也可以正常工作。

以下是该方法的示例用法。

首先,定义一个包含ListViewItem数据的类。

public class TestListViewItemClass
{
    public int TestInt { get; set; }

    public string TestString { get; set; }

    public DateTime TestDateTime { get; set; }

    public TimeSpan TestTimeSpan { get; set; }

    public decimal TestDecimal { get; set; }
}

然后,创建一个返回数据项的方法。只要它返回类类型的IEnumerable,此方法就可以查询数据库,调用Web服务API等等。

public IEnumerable<TestListViewItemClass> GetItems()
{
    for (int x = 0; x < 15000; x++)
    {
        yield return new TestListViewItemClass()
        {
            TestDateTime = DateTime.Now,
            TestTimeSpan = TimeSpan.FromDays(x),
            TestInt = new Random(DateTime.Now.Millisecond).Next(),
            TestDecimal = (decimal)x + new Random(DateTime.Now.Millisecond).Next(),
            TestString = "Test string " + x,
        };
    }
}

最后,在ListView所在的表单上,您可以填充ListView。出于演示目的,我使用表单的Load事件来填充ListView。很可能,你会希望在表格的其他地方这样做。

我已经包含了从我的类TestListViewItemClass的实例生成ListViewItem的函数。在生产场景中,您可能希望在其他地方定义该函数。

private async void TestListViewForm_Load(object sender, EventArgs e)
{     
    var function = new Func<TestListViewItemClass, ListViewItem>((TestListViewItemClass x) =>
    {
        var item = new ListViewItem();

        if (x != null)
        {
            item.Text = x.TestString;
            item.SubItems.Add(x.TestDecimal.ToString("F4"));
            item.SubItems.Add(x.TestDateTime.ToString("G"));
            item.SubItems.Add(x.TestTimeSpan.ToString());
            item.SubItems.Add(x.TestInt.ToString());
            item.Tag = x;

            return item;
        }

        return null;
    });

       PopulateListView<TestListViewItemClass>(this.listView1, function, GetItems(), progress);

 }

在上面的例子中,我在表单的构造函数中创建了一个IProgress对象,如下所示:

progress = new Progress<int>(value =>
{
    toolStripProgressBar1.Visible = true;

    if (value >= 100)
    {
        toolStripProgressBar1.Visible = false;
        toolStripProgressBar1.Value = 0;
    }
    else if (value > 0)
    {
        toolStripProgressBar1.Value = value;
    }
 });

我已经在ListView中使用这种方法多次填充ListView,我们在ListView中填充了12,000个项目,而且速度非常快。最重要的是,在触摸ListView进行更新之前,您需要从数据库中完全构建对象。

希望这很有帮助。

我在下面包含了该方法的异步版本,该版本调用了此帖子顶部显示的main方法。

public static Task PopulateListViewAsync<T>(ListView listView, Func<T, ListViewItem> func,
        IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
    return Task.Run(() => PopulateListView<T>(listView, func, objects, progress));
}

答案 2 :(得分:0)

您还可以尝试在更新期间将visible或enabled属性设置为false,并查看您是否更喜欢这些结果。 当然,在更新完成后将值重置为true。

另一种方法是创建一个覆盖列表框的面板。将其左,右,高度和宽度属性设置为与列表框相同,并在更新期间将其visible属性设置为true,在完成后将其设置为false。