2010-07-21 3 views
5

У меня есть следующий код, который в основном принимает значения из базы данных и заполняет список..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());      
    } 
} 

Проблема, что у меня есть то, что это многократно выполняется через короткие промежутки времени (каждый второй) и результаты в деталях в 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

Freeze означает что-то еще: это означает, что объект (в данном случае набор элементов) не изменится, пока он заморожен. В этом случае вы тут же меняете его! –

+1

Freeze был просто термином, который я использовал для объяснения моего требования. –

ответ

9

Как это:

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

} 
finally 
{ 
    lvwMyList.EndUpdate(); 
} 

Убедитесь, что вы вызываете lvwMyList.Items.Clear()послеBeginUpdate, если вы хотите, чтобы очистить список перед заполнением его.

+1

Он все еще «вспыхивает», когда вы очищаете элементы. То же самое происходит и с TreeView. – leppie

+1

Это, безусловно, делает то, что я просил. Единственная проблема заключается в том, что он фактически блокирует форму :-) –

+1

Устранение внутри beginupdate должно помешать ему мигать. Форма не заблокирована beginupdate, а вашим кодом, который добавляет новые элементы. Попробуйте извлечь все элементы из Db перед выполнением обновления. – jgauffin

0

Вы также можете попробовать установить видимые или активированные свойства на значение false во время обновления и посмотреть, хотите ли вы, чтобы эти результаты были лучше. Конечно, сбросьте значения до значения true, когда обновление выполнено.

Другой подход - создать панель для наложения списка. Задайте свойства слева, справа, высоты и ширины так же, как и ваш список, и установите для него видимое свойство значение true во время обновления, а false - после завершения.

+1

Отключение и включение управления, похоже, –

1

Это мой первый пост на StackOverflow, поэтому прошу прощения за неправильное форматирование кода ниже.

Чтобы предотвратить блокировку формы при обновлении ListView, вы можете использовать нижеприведенный метод, который я написал для решения этой проблемы.

Примечание: Этот метод не следует использовать, если вы планируете заполнить ListView более чем 20 000 элементов. Если вам нужно добавить в список List более 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; } 
} 

Затем создайте метод, который возвращает элементы данных. Этот метод может запрашивать базу данных, вызывать 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. Скорее всего, вы захотите сделать это в другом месте в форме.

Я включил функцию, которая генерирует ListViewItem из экземпляра моего класса TestListViewItemClass. В сценарии производства вы, скорее всего, захотите определить функцию в другом месте.

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 много раз в проектах, где мы были заселять вверх до 12 000 элементов в ListView, и это очень быстро. Главное, что вам нужно, чтобы ваш объект был полностью построен из базы данных, прежде чем вы даже коснетесь 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)); 
}