2010-07-21 7 views
5

Mam następujący kod, który w zasadzie pobiera wartości z bazy danych i zapełnia listę..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());      
    } 
} 

Problem, który mam jest to, że jest wykonywany wielokrotnie w krótkich odstępach czasu (co sekundę) i wyników w pozycji w ListView nieustannie znikają i pojawiają się ponownie. Czy jest jakiś sposób, aby zatrzymać odświeżanie listview, dopóki nie zostanie zrobione z aktualizacjami? Coś jak poniżej:

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

Zamrożenie oznacza coś innego: oznacza, że ​​obiekt (w tym przypadku zbiór elementów) nie ulegnie zmianie podczas jego zamrożenia. W takim przypadku natychmiast go modyfikujesz! –

+1

Zamrażanie było tylko terminem, którego używałem w celu wyjaśnienia mojego wymogu. –

Odpowiedz

9

Jak to:

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

} 
finally 
{ 
    lvwMyList.EndUpdate(); 
} 

Upewnij się, że powołują lvwMyList.Items.Clear()poBeginUpdate jeśli chcesz wyczyścić listę przed napełnieniem go.

+1

To nadal będzie "błyskać" podczas usuwania przedmiotów. To samo dzieje się na TreeView. – leppie

+1

To zdecydowanie robi to, o co prosiłem. Jedyny problem polega na tym, że praktycznie blokuje on również formę :-) –

+1

Mając wyraźne wnętrze beginupdate powinno się zapobiec migotaniu. Formularz nie jest zablokowany przez funkcję beginupdate, ale kod, który dodaje nowe elementy. Spróbuj pobrać wszystkie elementy z Db przed wykonaniem aktualizacji. – jgauffin

0

Możesz także spróbować ustawić wartość widoczną lub włączoną właściwości na wartość false podczas aktualizacji i sprawdzić, czy wyniki te są lepsze. Oczywiście, resetuj wartości do wartości true, gdy aktualizacja zostanie zakończona.

Innym podejściem jest utworzenie panelu, w którym zostanie nałożony listbox. Ustaw właściwości left, right, height i width tak samo jak listbox i ustaw właściwość visible na true podczas aktualizacji, false po zakończeniu.

+1

Wyłączenie i włączenie kontroli wydaje się pogarszać problem –

1

To jest mój pierwszy wpis na StackOverflow, więc przepraszam za niechlujny format kodu poniżej.

Aby zapobiec blokowaniu formularza podczas aktualizacji ListView, możesz użyć poniższej metody, którą napisałem, aby rozwiązać ten problem.

Uwaga: Ta metoda nie powinna być używana, jeśli użytkownik chce wypełnić ListView ponad 20 000 pozycji. Jeśli chcesz dodać więcej niż 20k elementów do ListView, rozważ uruchomienie ListView w trybie wirtualnym.

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

Nie trzeba dostarczyć przedmiot IProgress, wystarczy użyć nieważną i metoda będzie działać tak samo dobrze.

Poniżej znajduje się przykład zastosowania tej metody.

Najpierw zdefiniuj klasę zawierającą dane dla obiektu 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; } 
} 

Następnie utwórz metodę, która zwraca elementy danych. Ta metoda może wywoływać zapytania do bazy danych, wywoływać interfejs API usług sieci Web lub coś podobnego, o ile zwraca wartość IEnumerable danego typu klasy.

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

Wreszcie na formularzu, gdzie znajduje się Twój ListView, możesz wypełnić ListView. Do celów demonstracyjnych używam zdarzenia Load formularza, aby zapełnić listę ListView. Najprawdopodobniej będziesz chciał to zrobić gdzie indziej w formularzu.

Dołączyłem funkcję, która generuje obiekt ListViewItem z instancji mojej klasy, TestListViewItemClass. W scenariuszu produkcyjnym prawdopodobnie będziesz chciał zdefiniować funkcję w innym miejscu.

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

} 

W powyższym przykładzie, stworzyłem obiekt IProgress w konstruktorze formularza jest tak:

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

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

Użyłem tej metody wypełniania ListView wielokrotnie w projektach, w których byliśmy podczas wypełniania górę do 12 000 pozycji w ListView i jest niezwykle szybki. Najważniejsze jest, aby obiekt był w pełni zbudowany z bazy danych, zanim jeszcze dotkniesz ListView w poszukiwaniu aktualizacji.

Mam nadzieję, że jest to pomocne.

Dołączyłem poniżej wersję asynchroniczną metody, która wywołuje główną metodę pokazaną u góry tego wpisu.

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)); 
} 
Powiązane problemy