2010-07-21 98 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)); 
}