2010-07-21 100 views
5

我有以下代码,它基本上从数据库中获取值并填充listview。.NET 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());      
    } 
} 

,我的问题是,这是在反复短的间隔(每秒),并导致项目在ListView执行不断消失和重新出现。有什么方法可以阻止列表视图刷新,直到完成更新?类似下面:

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 
} 
+0

冻结意味着别的东西:它意味着对象(在这种情况下是元素的集合)在冻结时不会改变。在这种情况下,你马上修改它! – 2010-07-21 12:16:07

+1

冻结只是一个术语,用于解释我的要求 – 2010-07-21 12:26:22

回答

9

像这样:

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

} 
finally 
{ 
    lvwMyList.EndUpdate(); 
} 

请确保您调用lvwMyList.Items.Clear()BeginUpdate如果你想填充它之前清除列表。

+1

当您清除物品时它仍然会“闪现”。在TreeView上也是如此。 – leppie 2010-07-21 12:16:57

+1

这确实做了我所要求的。唯一的问题是,它实际上锁定了表单:-) – 2010-07-21 12:28:18

+1

在beginupdate内部清除应该防止它闪烁。该表单不会被beginupdate锁定,而会被添加新项目的代码锁定。尝试在更新之前从Db获取所有项目。 – jgauffin 2010-07-22 05:26:17

0

您还可以尝试在更新过程中将可见属性或启用属性设置为false,并查看是否更喜欢这些结果。 当然,更新完成后将值重置为true。

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

+1

禁用并启用控件似乎会使问题变得更糟 – 2010-07-22 08:12:55

1

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

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

注意:如果您希望用超过20,000个项目填充ListView,则不应使用此方法。如果您需要向ListView添加20多个项目,请考虑以虚拟模式运行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; } 
} 

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

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中的12,000个项目,并且速度非常快。主要的是你需要从数据库完全构建你的对象,然后你甚至可以触摸ListView进行更新。

希望这是有帮助的。

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

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)); 
}