проблема, вероятно, проста, сообщение длиннее, чем я хотел, но я попытался предоставить как можно больше информации и деталей.
Я не писал это приложение с графическим интерфейсом и не проектировал, однако, как и большинство из нас, я унаследовал его.
У него был (обычный) ListView, на самом деле приложение имеет несколько ListView, не уверен, что это имеет значение.
Поскольку количество элементов, поступающих в этот ListView (экран / форму), может стать очень большим 10K +, я решил преобразовать его в виртуальный список, однако у меня возникли некоторые ранние проблемы.
Одна из самых больших проблем заключается в том, что элементы заполняются асинхронно при нажатии кнопки в форме.
Когда они прибывают (из службы / сети / базы данных), элементы встраиваются в ListViewItem (s) и добавляются в someListItems, который является ArrayList.
В моем методе RetrieveVirtualItem мне нужно обработать оба случая, когда список пуст и когда у меня уже есть что-то (после нажатия кнопки), и именно тогда я ударился о стену (без каламбура)
со следующей строкой кода:
if ( someListItems.Count > e.ItemIndex )
В основном это вызывает (не знаю почему) вызов метода Dispose в главной форме, что приводит к серьезному сбою всего приложения. НО !!, это происходит только тогда, когда я нажимаю на форму и список. Если форма только что загружена и заполнена, то все в порядке .. после второго щелчка левой кнопкой мыши, BOOM!
Мне потребовалось несколько часов, чтобы понять, что приведенная выше строка была виновником, поскольку стек вызовов был не очень очевиден, чтобы указать на это, и еще одна минута, чтобы выяснить, что e.ItemIndex
является виновником. Но почему??? я
n MSDN примеры, они получают доступ к e.ItemIndex для выполнения тестов, и это выглядит нормально.
Виртуальный режим устанавливается в конструкторе вида:
myListView.VirtualMode = true;
VirtualListSize устанавливается сразу после асинхронного поступления данных:
myListView.VirtualListSize = someArrayList.Count;
Это моя реализация RetrieveVirtualItem:
private void blah_RetrieveVirtualItem( object sender, RetrieveVirtualItemEventArgs e )
{
// someListItems is an ArrayList that is created when the object/class loads..and populated with ListViewItems.
// i.e. private ArrayList someListItems = new ArrayList();
// it is populated asynchronously by hitting a button on the form, hence it's empty when the form loads..
if ( someListItems.Count <= 0 )
{
e.Item = new ListViewItem( "" );
e.Item.SubItems.Add( "" );
e.Item.SubItems.Add( "" );
}
else
{
// the of code below is the problem, and more specifically - e.ItemIndex causes somehow to call Dispose on the main form..
// the reason I have this code is because if I take it out, all items will show up, no problem, but it will crash when I try to scroll down..
// with message like this:
// Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
if ( someListItems.Count > e.ItemIndex )
{
// took out my code out to eliminate possibility that it's my code. :)
int x = e.ItemIndex * e.ItemIndex;
e.Item = new ListViewItem( x.ToString() );
// but I had something like that just for a test:
// ListViewItem item = ( ListViewItem )someListItems[e.ItemIndex];
// e.Item = item;
// remember that someListItems already has ListViewItems
}
}
}
Метод, который вызывается асинхронно, создает ListViewItems и заполняет someListItems, выглядит примерно так:
private void ExampleMethod_That_PopulatesSomeArrayList(ArrayList ar)
{
//Im only showing more essential code..
SomeArrayList.Items.Clear();
myListView.VirtualListSize = ar.Count;
foreach ( SomeObject o in ar )
{
ListViewItem lvi = new ListViewItem( SomeObject.somePropertyID, 0 );
// I've tried changing the above line to: lvi = new ListViewItem( SomeObject.somePropertyID, 0 ); // and having the ListViewItem lvi on the class level. i.e private ListViewItem lvi
// didn't help.. :(
lvi.SubItems.Add( o.someProperty1 );
lvi.SubItems.Add( o.someProperty2 );
// there's quite few of these subitems..2 is enough for this example...
}
// the orignal code, before I changed it to virtual list was adding the items somewhere here..after finished looping, now I'm just trying to reuse that array of ListViewItems.
}
Есть еще одна проблема, из-за которой предметы не появляются вообще, если я не вынимаю:
if ( someListItems.Count > e.ItemIndex )
но при попытке прокрутки у меня возникает проблема с индексом вне диапазона.
ОБНОВЛЕНИЕ:
Я заметил, что если я устанавливаю размер виртуального списка, только после завершения цикла и, следовательно, он равен нулю (0) в начале (я всегда могу сбросить его до нуля), то все работает и не Не нужно проверять размер, все, что мне нужно сделать, это:
После цикла: private void ExampleMethod_That_PopulatesSomeArrayList(ArrayList ar)
this.myListView.VirtualListSize = someListItems.Count;
, который я хотел бы поблагодарить за Ганса Пассанта за то, что он заметил несоответствие.
Итак, на данный момент это все (я уверен, что я добавлю некоторый код или внесу изменения, поскольку я хотел бы добавить некоторое кэширование, но по крайней мере у меня есть кое-что ...
private void blah_RetrieveVirtualItem( object sender, RetrieveVirtualItemEventArgs e )
{
e.Item = ( ListViewItem )someListItems[e.ItemIndex];
}
Единственное, что я не уверен в том, что упомянул Ханс Пассант, это: "этот обработчик событий не может нормально выделять ListViewItem." , что я не уверен, если я понять, потому что ListViewItems выделены и вставлены в массив someListItems . У меня есть попытка поймать вокруг, и я сделал это раньше.
Кроме того, я подумал и был бы признателен за чей-то вклад в эту идею:
Создать отдельный объект, который будет содержать все свойства SomeObject или вставить SomeObject (ы) в список и создать новые ListViewItems по мере необходимости?
например:
private void blah_RetrieveVirtualItem( object sender, RetrieveVirtualItemEventArgs e )
{
// that list would be build sometime during the loop iteration in
// (I'm using the original method name mentioned way above in this post)
// ExampleMethod_That_PopulatesSomeArrayList(ArrayList ar)
SomeObject o = listOfObjects[e.ItemIndex];
e.Item = new ListViewItem();
e.Item.SubItems.Add(o.prop1);
e.Item.SubItems.Add(o.prop2);
e.Item.SubItems.Add(o.prop3);
}