С# обновить список без «задержек»

Я искал в Интернете способ сделать это без особого успеха, поэтому вот вопрос.

Как добавить элементы в список в отдельном потоке, чтобы он не зависал в пользовательском интерфейсе? Каждый раз в списки добавляется примерно 5-15 тысяч элементов, и каждый раз пользовательский интерфейс зависает на 5-12 секунд.

Форма имеет 4 списка, информация для этих списков сначала создается и добавляется в 2D-массив (это упрощает отслеживание всей информации, которая находится вместе в 1 строке). после чего я перебираю этот 2D-массив, добавляя 4 столбца в 1 строку в соответствующий список.

eg.

for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}

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


person Raskaroth    schedule 05.02.2012    source источник
comment
Вы уверены, что простой список — лучший пользовательский интерфейс для показа пользователю пятнадцати тысяч элементов? Нет никакого способа, которым они должны видеть список всех из них сразу.   -  person Matti Virkkunen    schedule 06.02.2012
comment
Причина, по которой я хочу отобразить так много элементов, заключается в целях отладки, чтобы проверить, действительно ли вся информация верна и что я ожидаю от нее. эта программа позже будет интегрирована в другую программу, которая вообще не отображает элементы. Мне просто любопытно, как это сделать, если это было возможно (что, оказывается, так и есть). Однако я посмотрю Listviews   -  person Raskaroth    schedule 06.02.2012


Ответы (7)


Вы можете использовать другой подход и вместо этого использовать виртуальный ListView. Когда ListView является «виртуальным», вы несете ответственность за ведение списков элементов и указание ListView, что отображать в событиях рисования. Таким образом, вы можете обновлять свои списки любым потокобезопасным способом, который вам нравится, и ListView будет запрашивать у вас только «что рисовать на экране», а не полный список элементов. На самом деле это предпочтительный метод, когда получение списка всех элементов очень дорого.

См. документацию по VirtualMode:

http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.virtualmode.aspx

ОБНОВЛЕНИЕ: Поскольку вы упомянули, что вам это нужно для отладки, я также предлагаю вам использовать вывод отладки (Debug.WriteLine()), который может быть более подходящим для работы. Он оптимизирован, потокобезопасен, не блокирует ничего, кроме самого себя, и самое приятное то, что он не влияет на производительность релизных сборок.

А если вам нужен более производительный вывод, вы можете перенаправить вывод отладки куда угодно.

person Sedat Kapanoglu    schedule 05.02.2012
comment
Спасибо! Я буду изучать это, единственная причина, по которой я хочу знать, есть ли лучший способ быстрого отображения без задержки пользовательского интерфейса.. Просто чтобы я знал. не показывать пользователю, так как он никогда его не увидит. - person Raskaroth; 06.02.2012

Используйте BeginUpdate() и EndUpdate()

http://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.beginupdate.aspx

person Bruno Silva    schedule 05.02.2012

Это по своей сути невозможно.
Вы можете управлять пользовательским интерфейсом только из потока пользовательского интерфейса. (пользовательский интерфейс не является потокобезопасным)

Вы можете сделать это быстрее, вызвав BeginUpdate() и EndUpdate(), и вы можете сделать это еще быстрее (но сложнее), используя ListView в виртуальном режиме.

Однако вы не должны отображать 15 000 элементов в списке.
Такой список будет бесполезен в реальном использовании.

person SLaks    schedule 05.02.2012
comment
ListBox не поддерживает виртуальный режим. Смотрите мой ответ. - person Sedat Kapanoglu; 06.02.2012

listBox1.BeginUpdate();
listBox2.BeginUpdate();
listBox3.BeginUpdate();
listBox4.BeginUpdate();

for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}


listBox1.EndUpdate();
listBox2.EndUpdate();
listBox3.EndUpdate();
listBox4.EndUpdate();

Это задержит отрисовку до тех пор, пока не будут добавлены все элементы, поэтому это должно быть быстрее.

person Brad    schedule 05.02.2012

Вы должны попробовать использовать методы BeginUpdate/EndUpdate:

listbox1.BeginUpdate();
// Adds 5K items
listbox1.EndUpdate();
person ken2k    schedule 05.02.2012

Вы можете значительно ускорить этот процесс, предотвратив перерисовку ListBox после каждого добавления, заключив цикл for в:

listBox1.BeginUpdate();
listBox2.BeginUpdate();
listBox3.BeginUpdate();
listBox4.BeginUpdate();
for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}
listBox1.EndUpdate();
listBox2.EndUpdate();
listBox3.EndUpdate();
listBox4.EndUpdate();
person Robert Rouhani    schedule 05.02.2012
comment
+1, ответ кажется мне достаточно хорошим. Также интересно узнать причину понижения. - person Bruno Silva; 06.02.2012
comment
Я не минусовал, однако подозреваю, что это из-за другого поста перед вашим. который публикуется одновременно. - person Raskaroth; 06.02.2012

Я просто хочу собрать все ответы вместе. Вы действительно должны обновлять элементы в своем потоке графического интерфейса, иначе вы можете получить неожиданные результаты. Чтобы гарантировать, что код работает в потоке графического интерфейса, и ваш список не перерисовывается при каждом добавлении(), вам нужна пара BeingUpdate() и EndUpdate(), как уже писали другие, но для запуска этого в потоке графического интерфейса используйте BeingInvoke(), который просто помещает задачу в «очередь потоков GUI», готовую к использованию. BeingInvoke() немедленно вернется, но ваш запрос будет поставлен в очередь.

Документ API BeingInvoke(). http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

хорошее обсуждение BeginInvoke() и почему он используется.

BeginInvoke((MethodInvoker)delegate
{
    listBox1.BeginUpdate();
    listBox2.BeginUpdate();
    listBox3.BeginUpdate();
    listBox4.BeginUpdate();
    for (int n = 0; n < 7500; n++)
    {
        listBox1.Items.Add(itemList[n, 0].ToString());
        listBox2.Items.Add(itemList[n, 1].ToString());
        listBox3.Items.Add(itemList[n, 2].ToString());
        listBox4.Items.Add(itemList[n, 3].ToString());
    }
    listBox1.EndUpdate();
    listBox2.EndUpdate();
    listBox3.EndUpdate();
    listBox4.EndUpdate();
});
person Dave    schedule 06.02.2012