listview虚拟列表,e.ItemIndex崩溃!

时间:2010-10-08 15:53:22

标签: c# listview listviewitem

问题可能很简单,帖子比我希望的要长,但我尝试提供尽可能多的信息和细节。 我没有编写这个GUI应用程序,也没有设计,但是像我们大多数人一样,我继承了它。

它有一个(常规)ListView,实际上该应用程序有几个ListView,不确定是否重要。 因为到达这个ListView(屏幕/窗体)的项目数量可以变得非常大10K +我决定将其转换为虚拟列表,但是我遇到了一些早期问题。

最大的问题之一是,通过点击表单上的按钮异步填充项目。  当它们到达时(来自服务/网络/数据库),这些项目被构建到ListViewItem中,并被添加到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;

我要感谢 Hans Passant 注意到这种差异。 所以这是完整的,现在(我确定我会添加一些代码或更改,因为我想添加一些缓存,但至少我有一些东西......

private void blah_RetrieveVirtualItem( object sender, RetrieveVirtualItemEventArgs e )
 {
   e.Item = ( ListViewItem )someListItems[e.ItemIndex];
}

我唯一不确定Hans Passant提到的是:“这个事件处理程序从不分配ListViewItem真的不行。”我不确定是否我理解,因为 ListViewItems 已分配并插入 someListItems 数组。我确实试过了,我之前做过。

此外,我在想,我很感激有人对这个想法的意见: 创建一个单独的对象,它将包含SomeObject的所有属性或将SomeObject插入List并根据需要创建新的ListViewItems? 的 e.g:

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

1 个答案:

答案 0 :(得分:1)

回答这个问题。虚拟列表崩溃,因为未正确设置VirtualListSize。 基本上,为了帮助其他人,如果您有虚拟列表,请始终确保VirtualListSize对应于您尝试显示的实际项目数。如果不是,那么一切都会破裂。如果您确实更新,删除,添加任何内容,则需要将VirtualListSize重置为正确的数字。

我最终从ListView派生并将我的listviewitems存储在一个数组中。