2010-07-21 27 views
5

Tôi có đoạn mã sau, về cơ bản lấy các giá trị từ một cơ sở dữ liệu và điền vào một listview..NET Listview Refresh

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

Vấn đề mà tôi có là việc này liên tục được thực hiện trong khoảng thời gian ngắn (mỗi giây) và dẫn đến các mục trong listview liên tục biến mất và tái xuất hiện. Có cách nào để ngăn chặn listview làm mới cho đến khi nó được thực hiện với các bản cập nhật? Một cái gì đó như dưới đây:

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

Cố định có nghĩa là đối tượng khác (trong trường hợp này là tập hợp các phần tử) sẽ không thay đổi khi bị đóng băng. Trong trường hợp này, bạn ngay lập tức sửa đổi nó! –

+1

Freeze chỉ là một thuật ngữ mà tôi đã sử dụng cho mục đích giải thích yêu cầu của tôi –

Trả lời

9

Như thế này:

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

} 
finally 
{ 
    lvwMyList.EndUpdate(); 
} 

Hãy chắc chắn rằng bạn gọi lvwMyList.Items.Clear()sauBeginUpdate nếu bạn muốn xóa danh sách trước khi làm đầy nó.

+1

Nó vẫn sẽ 'nhấp nháy' khi bạn xóa các mục. Tương tự xảy ra trên TreeView. – leppie

+1

Điều này chắc chắn làm những gì tôi yêu cầu. Chỉ có vấn đề là nó hầu như khóa các hình thức là tốt :-) –

+1

Có rõ ràng bên trong startupdate nên ngăn chặn nó nhấp nháy. Biểu mẫu không bị khóa bởi beginupdate nhưng do mã của bạn thêm các mục mới. Hãy thử tìm nạp tất cả các mục từ Db trước khi thực hiện cập nhật. – jgauffin

0

Bạn cũng có thể thử đặt thuộc tính hiển thị hoặc đã bật thành sai trong quá trình cập nhật và xem bạn có thích các kết quả đó tốt hơn không. Tất nhiên, đặt lại giá trị thành true khi cập nhật xong.

Cách tiếp cận khác là tạo bảng điều khiển để che phủ hộp danh sách. Đặt các thuộc tính bên trái, phải, chiều cao và chiều rộng giống như hộp danh sách của bạn và đặt thuộc tính hiển thị thành đúng trong quá trình cập nhật, sai sau khi bạn hoàn tất.

+1

Vô hiệu hóa và cho phép kiểm soát dường như làm cho vấn đề trở nên tồi tệ hơn –

1

Đây là lần đầu tiên tôi đăng trên StackOverflow, vì vậy hãy tha thứ cho định dạng mã lộn xộn bên dưới.

Để ngăn chặn khóa biểu mẫu trong khi cập nhật ListView, bạn có thể sử dụng phương pháp bên dưới mà tôi đã viết để giải quyết vấn đề này.

Lưu ý: Phương pháp này không nên được sử dụng nếu bạn dự kiến ​​đưa vào ListView với hơn 20.000 mục. Nếu bạn cần thêm nhiều hơn 20k mục vào ListView, hãy xem xét chạy ListView ở chế độ ảo.

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

Bạn không phải cung cấp đối tượng IProgress, chỉ cần sử dụng null và phương thức cũng sẽ hoạt động.

Dưới đây là ví dụ về cách sử dụng phương pháp.

Đầu tiên, hãy xác định lớp có chứa dữ liệu cho 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; } 
} 

Sau đó, tạo phương thức trả về các mục dữ liệu của bạn. Phương thức này có thể truy vấn một cơ sở dữ liệu, gọi một API dịch vụ web, hoặc bất cứ điều gì, miễn là nó trả về một IEnumerable loại lớp của bạn.

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

Cuối cùng, trên biểu mẫu mà ListView của bạn cư trú, bạn có thể điền ListView. Đối với mục đích trình diễn, tôi đang sử dụng sự kiện Load của biểu mẫu để điền vào ListView. Nhiều khả năng, bạn sẽ muốn làm điều này ở nơi khác trên biểu mẫu.

Tôi đã bao gồm hàm tạo ra một ListViewItem từ một thể hiện của lớp của tôi, TestListViewItemClass. Trong một kịch bản sản xuất, bạn có thể sẽ muốn xác định hàm ở nơi khác.

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

} 

Trong ví dụ trên, tôi tạo ra một đối tượng IProgress trong constructor của form như thế này:

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

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

Tôi đã sử dụng phương pháp này Populating một ListView nhiều lần trong các dự án mà chúng tôi đã Populating lên đến 12.000 mục trong ListView, và nó cực kỳ nhanh. Điều chính là bạn cần phải có đối tượng của bạn được xây dựng hoàn toàn từ cơ sở dữ liệu trước khi bạn thậm chí chạm vào ListView để cập nhật.

Hy vọng điều này hữu ích.

Tôi đã bao gồm bên dưới phiên bản không đồng bộ của phương thức, gọi phương thức chính được hiển thị ở đầu bài đăng này.

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)); 
} 
Các vấn đề liên quan