0


0

listview仮想リスト、e.ItemIndexがクラッシュします!

問題はおそらく単純で、投稿は思ったよりも長いですが、できるだけ多くの情報と詳細を提供しようとしました。 私はこのGUIアプリを書いたり、設計したりしませんでしたが、私たちのほとんどのように、私はそれを継承しました。

(通常の)ListViewがあり、実際にはアプリに複数のListViewがありますが、それがまだ重要かどうかはわかりません。 この1つのListView(画面/フォーム)に到着するアイテムの数が非常に大きい10K +になる可能性があるため、仮想リストに変換することにしましたが、いくつかの初期の問題が発生しています。

最大の問題の1つは、フォーム上のボタンを押すことでアイテムが非同期に入力されることです。 それらが(サービス/ネットワーク/データベースから)到着すると、アイテムはListViewItemに組み込まれ、ArrayListであるsomeListItemsに追加されます。

RetrieveVirtualItemメソッドでは、リストが空の場合と、ボタンを押した後何かが既にある場合と、次のコード行で壁にぶつかった場合(しゃれはありません)の両方の場合を処理する必要があります。

if ( someListItems.Count > e.ItemIndex )

基本的に、メインフォームでDisposeメソッドを呼び出すと(理由はわかりません)、アプリケーション全体が激しくクラッシュします。 しかし、フォームとリストをクリックしたときにのみ発生します。 フォームがロードされて読み込まれただけであれば問題ありません。マウスを左クリックすると、BOOM!

呼び出しスタックがそれを指摘するほど明白ではなかったため、上の行が犯人であることを理解するのに数時間かかり、「e.ItemIndex」が犯人であることを見つけるのにもう1分かかりました。 しかし、なぜ??? テストを実行するためにe.ItemIndexにアクセスするmsdnの例はありますが、問題ないようです。

仮想モードは、次の形式のコンストラクターで設定されます。

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をリストに挿入し、必要に応じて新しいListViewItemを作成しますか? 例えば:

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 Answer


1


この質問に答えるために。 VirtualListSizeが正しく設定されていなかったため、仮想リストがクラッシュしていました。 基本的に、ここで他の人を助けるために、仮想リストがある場合は、VirtualListSizeが表示しようとしている実際のアイテムの数に対応していることを常に確認してください。 そうでない場合、すべての地獄が緩んで壊れます。 更新、削除、追加などを行う場合は、VirtualListSizeを正しい数値にリセットする必要があります。

最終的にはListViewから派生し、listviewitemsを配列に格納しました。