今日は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を使ってソートし、リストビューにアイテムを詰めなおす作戦をとったよ。これでも十分に機能するね。