none
リストビュー(アイコン表示)でのカスタムドローについて RRS feed

  • 質問

  • リストビューのカスタムドローで、スクロールを下の方にするとどんどん描画が遅くなります。何か解決策はありませんでしょうか ?
    テストとして、単純なダイアログのMFCアプリに、リストビューを張り、アイコン表示、スクロールバーは右に出るように設定しました。

        CONTROL         "",IDC_LIST1,"SysListView32",LVS_AUTOARRANGE | WS_BORDER | WS_TABSTOP,7,7,252,186

    ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, &CListViewTestDlg::OnCustomDraw)

    BOOL CListViewTestDlg::OnInitDialog()
    の中で
     LVITEM item;
     item.iSubItem = item.iImage = item.iItem =0;
     item.mask = LVIF_IMAGE | LVIF_PARAM;

     m_ListCtrl.SetItemCount( 5000 );
     for ( int i = 0; i < 5000; ++i ) {
      item.iImage = i;
      item.iItem = i;
      item.lParam = i;
      m_ListCtrl.InsertItem( &item );
     }
    と、試しに5000件のデータを入れました。

    カスタム描画は
    void CListViewTestDlg::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult)
    {
     NMLVCUSTOMDRAW* pNMCD = (NMLVCUSTOMDRAW*)pNMHDR;
     *pResult = 0;

     switch( pNMCD->nmcd.dwDrawStage ) {
     case CDDS_PREPAINT:
      *pResult = CDRF_NOTIFYITEMDRAW;
      break;
     case CDDS_ITEMPREPAINT:
     {
      CDC*  pDC = CDC::FromHandle ( pNMCD->nmcd.hdc );
      const int  nItem = (int)pNMCD->nmcd.dwItemSpec;
      switch( nItem / 1000 ) {
      case 0:  pDC->SetTextColor( RGB(0x00,0x00,0x00) ); break;
      case 1:  pDC->SetTextColor( RGB(0xFF,0x00,0x00) ); break;
      case 2:  pDC->SetTextColor( RGB(0xFF,0xFF,0x00) ); break;
      case 3:  pDC->SetTextColor( RGB(0x00,0xFF,0x00) ); break;
      case 4:  pDC->SetTextColor( RGB(0x00,0xFF,0xFF) ); break;
      }
      if ( nItem == 0 ) {
       TRACE( "CDDS_ITEMPREPAINT : 0\n" );
      } else
      if ( nItem == 0 ) {
       TRACE( "CDDS_ITEMPREPAINT : 0\n" );
      } else
      if ( nItem == 1000 ) {
       TRACE( "CDDS_ITEMPREPAINT : 1000\n" );
      } else
      if ( nItem == 2000 ) {
       TRACE( "CDDS_ITEMPREPAINT : 2000\n" );
      } else
      if ( nItem == 3000 ) {
       TRACE( "CDDS_ITEMPREPAINT : 3000\n" );
      }
      CRect rcIcon;
      m_ListCtrl.GetItemRect( nItem, &rcIcon, LVIR_ICON );
      CString www;
      www.Format( _T("%d"), nItem );
      pDC->DrawText( www, &rcIcon, DT_CENTER | DT_SINGLELINE | DT_VCENTER );
      *pResult = CDRF_NOTIFYPOSTPAINT;
     }
      break;
     case CDDS_ITEMPOSTPAINT:
     {
      const int  nItem = (int)pNMCD->nmcd.dwItemSpec;
      *pResult = CDRF_SKIPDEFAULT;
     }
      break;
     }
    }

    としました。
    どうも、スクロールバーが一番下になると、CDDS_ITEMPOSTPAINTが、全件(今回は5000件)ループするようです。
    スクロールバーが一番上の方にある場合には、表示されているアイテム分だけしかループせず、スピードが速いようです。

    デバッグウィンドウに、上の方だと
    CDDS_ITEMPREPAINT : 0
    だけですが、下の方に行くと、
    CDDS_ITEMPREPAINT : 3000
    CDDS_ITEMPREPAINT : 2000
    CDDS_ITEMPREPAINT : 1000
    CDDS_ITEMPREPAINT : 0
    と全て出てきます。
    どう考えても、3000件目を描画するのであれば、0件目は表示範囲外の筈です。

    カスタムドロー時に、rcIconがClientRectに入っているかどうかをチェックすると早くはなりますが、5000件が、3万件等と
    なったりすると、このループの量は気になります。

    正常に表示範囲のみ、カスタムドローが呼ばれるようにするにはどのようにすればよいのでしょうか ?

    現在、VS2005、Windows Vista SP1 64bit を使っています。
    プロジェクトはマルチバイト文字セットを使用し、他OSや他VSでは試していません。


    Sorry, I am not good at English.
    2010年12月13日 11:42

回答

  • 少し見てみました。

    MFCでもAPIでも再現しますね。再現のポイントは縦スクロールバーです。縦スクロールバーをスクロールすると、この現象が発生します。横スクロールバーのスクロールだと再現しません。まだ確信はありませんが、Common Controlの不具合のように思えます。Connectに投げたほうが良いかもしれません。

    仮想リストビューでは再現しないため、仮想リストビューの採用を検討してみては如何でしょうか。何れにせよ、大量のデータを扱うのなら、仮想リストビューのほうが都合がよいですよ。

    • 回答としてマーク FC-Shiro 2010年12月14日 10:59
    2010年12月13日 22:25
  • リストコントロール(ビュー)に描画を任すと遅くて使い物になりません。
    せいぜい1000行程度が実用範囲です。
    自分の場合は、これを超えることがほとんどなので、
    LVS_OWNERDRAWFIXED するか、LVS_OWNERDATA するか、両方指定して、
    全て自分で描画してます。まったくストレスなくスクロールする上に、
    好きなように描画できるのが魅力です。
    ここ何年かはリストコントロール自身に描画させたことはありません(笑)。
    • 回答としてマーク FC-Shiro 2010年12月14日 10:59
    2010年12月14日 3:31

すべての返信

  • 少し見てみました。

    MFCでもAPIでも再現しますね。再現のポイントは縦スクロールバーです。縦スクロールバーをスクロールすると、この現象が発生します。横スクロールバーのスクロールだと再現しません。まだ確信はありませんが、Common Controlの不具合のように思えます。Connectに投げたほうが良いかもしれません。

    仮想リストビューでは再現しないため、仮想リストビューの採用を検討してみては如何でしょうか。何れにせよ、大量のデータを扱うのなら、仮想リストビューのほうが都合がよいですよ。

    • 回答としてマーク FC-Shiro 2010年12月14日 10:59
    2010年12月13日 22:25
  • リストコントロール(ビュー)に描画を任すと遅くて使い物になりません。
    せいぜい1000行程度が実用範囲です。
    自分の場合は、これを超えることがほとんどなので、
    LVS_OWNERDRAWFIXED するか、LVS_OWNERDATA するか、両方指定して、
    全て自分で描画してます。まったくストレスなくスクロールする上に、
    好きなように描画できるのが魅力です。
    ここ何年かはリストコントロール自身に描画させたことはありません(笑)。
    • 回答としてマーク FC-Shiro 2010年12月14日 10:59
    2010年12月14日 3:31