MFCリストビューの簡易ラッパークラスを作ってみたよ

今日はMFCのリストビューに関する覚書だよ。

上図のように、MFCリストビューのレポート描画において、カラムやアイテムの追加が簡単にできるラッパーテンプレートクラスを作成したよ。

#include <sstream>

class ListCtrl
{
public:
  ListCtrl( CListCtrl* lv )
    : _lv(lv)
    , _isubitem(0)
    , _iitem(0)
    , _icolumn(0){}
  ~ListCtrl( void ){}
  void newRow( void ){ _isubitem=0; ++_iitem; }
  void insertColumn( const char* s, const int w=-1 )
  {
    LV_COLUMN lvc;
    lvc.mask=LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
    {
      lvc.iSubItem = _icolumn;// カラム番号
      lvc.cx = w;// カラムのピクセル幅
      lvc.pszText = const_cast<char*>(s);// 文字列
      lvc.fmt = LVCFMT_CENTER;//カラム文字列のフォーマット
    }
    _lv->InsertColumn(_icolumn, &lvc);
    ++_icolumn;
  }
  void autoColumnWidth( void )
  {
    // 既存カラムの幅を自動調節します。
    int n_column = _lv->GetHeaderCtrl()->GetItemCount();
    for( int i=0; i<n_column; ++i ){
      this->_lv->SetColumnWidth( i, LVSCW_AUTOSIZE_USEHEADER );
    }// i
  }
  template< class Pred, class Obj >
  void setItem( Pred& pred, Obj& obj )
  {
    LVITEM       lvi;
    string hi;
    {
      lvi.mask = LVIF_TEXT;
      lvi.iItem = _iitem; // 0ベースの行位置
      lvi.iSubItem = _isubitem;// 1べースのカラム位置
      {
        ostringstream os;
        os<< pred( obj );
        hi = os.str();
      }
      lvi.pszText = const_cast< char* >( hi.c_str() );
    }
    ( _isubitem==0 ) ? 
      _lv->InsertItem( &lvi ): _lv->SetItem( &lvi );
    ++_isubitem;
    return;
  }
private:
  int _isubitem, _iitem, _icolumn;
  CListCtrl* _lv;
};

さあこれは何だろうね?
これの使い方は以下の通りだ。まずはカラムの追加。

  // リストビューの初期化
  this->m_list2.SetExtendedStyle(LVS_EX_FULLROWSELECT);
  {
    // カラムの追加(リストビュー)
    ListCtrl lv( &this->m_list2 );// 本ラッパークラスを召還
    lv.insertColumn( "Planet" );   
    lv.insertColumn( "Distance" );
    lv.insertColumn( "Radius" );
    lv.insertColumn( "Mass" );    
    lv.insertColumn( "Image" );
  }

次にアイテムの追加。

  // アイテムの更新(リストビュー)
  this->m_list2.DeleteAllItems();
  {
    // アイテム(行)の追加
    ListCtrl lv( &this->m_list2 );// 本ラッパークラスを召還
    for each( Planet* planet in this->m_planet_vec ){
      lv.setItem( mem_fun( &Planet::Name ), planet );
      lv.setItem( mem_fun( &Planet::Distance ), planet );
      lv.setItem( mem_fun( &Planet::Mass ), planet );
      lv.setItem( mem_fun( &Planet::Radii ), planet );
      lv.setItem( mem_fun( &Planet::ImageFileName ), planet );
      lv.newRow();
    }// planet
    lv.autoColumnWidth();
  }

それでできたのが冒頭の写真だ。どうだろう。これだけの簡単なコードでリストビューが構築できたよ。
こうして作ったリストビューの各カラムのヘッダを押すとLVN_COLUMNCLICKコールバックに飛んでくるので、そのコールバックの中でどのカラムかを判定し、ソートしてやればよいというわけさ。
おっと、ソートだけどおじさんはCListCtrlのソート機能なんかは使わずに、上記の例で言えば、m_planet_vec を、自分の好きなようにSTLを使ってソートし、リストビューにアイテムを詰めなおす作戦をとったよ。これでも十分に機能するね。