frostar@wiki

メニュー

作ったもの

過去の遺物
TRPG
プログラム
リンク

自分関係

ここのページへのリンクについてはこちら
TRPGのセッション記録とかメインのブログ
生配信チャンネル。プログラム<雑談
気が向いたときにやります
個人的な質問に答えてます。

TRPG系

お世話になっているオンラインセッションサイト
TRPGのことならここ!
IRCサーバなど、お世話になってます。
ネクロニカの支援サイトです。ツール開発やオンセもやっています。

プログラム系

初心者のためのプログラムのページ。WindowsSDKの方についても詳しく書いています。
windowsSDKのテクニックがいろいろ書いてあります。
C++STL(標準テンプレートライブラリ)の日本語版リファレンスです。


更新履歴

取得中です。


チェックボックス付きリストビュー


※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

リストビューにチェックボックスを付けるには拡張スタイルを使用する。
拡張スタイルを使用するにはComctl32.dll(ver4.70以降)を使用する。
Comctl32.dllを使用する際には、最初に初期化が必要である。
InitCommonControls();
実際にチェックボックスを付けるときは作成したリストビューにLVS_EX_CHECKBOXESスタイルを付ければいい。
ListView_SetExtendedListViewStyle(hList,ListView_GetExtendedListViewStyle(hList) | LVS_EX_CHECKBOXES);
hList:リストビューのハンドル
リストビューに付けられたチェックボックスの情報を取得するにはListView_GetCheckStateマクロを使用する。
ListView_GetCheckState(hList); //戻り値:BOOL チェックされていたらtrueを返す
逆にチェックを付けたり、外したりするときはListView_SetCheckStateマクロを使用する。
ListView_SetCheckState(hList,index,bool); 
index:操作を行う列
bool:trueならチェックする、falseなら外す
ただし、ListView_GetCheckStateマクロは定義されていないこともあるようなので、その時は自分でマクロを定義する。
#define ListView_SetCheckState(hwndLV, i, fCheck) \
  ListView_SetItemState(hwndLV, i, INDEXTOSTATEIMAGEMASK((fCheck)?2:1), LVIS_STATEIMAGEMASK)
#endif

しかし、この方法では列の先頭にしかチェックボックスを付けることができない。
いろいろな場所に付けたり複数付けるためにはオーナードローを使用する。
オーナードローを使うためにはまずListViewにLVS_OWNERDRAWFIXEDスタイルを付ける。
ListView_SetExtendedListViewStyle(hList,ListView_GetExtendedListViewStyle(hList) | LVS_OWNERDRAWFIXED);
そして目的のリストビューの親のプロシージャでWM_DRAWITEMメッセージが来たときに描画処理を行うことで実現できる。
case WM_DRAWITEM:
//描画処理
break;
描画処理の内容は以下のようになる。
LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;//lParam:WM_DRAWITEMメッセージのlParam
HWND hList=lpDraw->hwndItem; //リストのハンドル
HDC hdc = lpDraw->hDC; //リストのデバイスコンテキスト
HBRUSH hBrush;//背景描画用ブラシ
char Text[256];//文字格納用

SaveDC(hdc);//デバイスコンテキストを保存
SetBkMode(hdc,OPAQUE);//デバイスコンテキストの設定(文字背景を不透過にする)
//背景色と文字色の設定
if (lpDraw->itemState & ODS_SELECTED) {//選択アイテムがあるなら強調表示
 hBrush=CreateSolidBrush (GetSysColor(COLOR_HIGHLIGHT));
 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}else{
 COLORREF Color=ListView_GetTextBkColor(hList);
 if(Color & 0xff000000) Color=GetSysColor(COLOR_WINDOW);
 hBrush=CreateSolidBrush (Color);
}
//アイテム数の取得
int SubItemNum=Header_GetItemCount(ListView_GetHeader(hList))-1;
//表示
for(SubItem=0;SubItem<=SubItemNum;SubItem++){
 //背景のクリア
 ListView_GetSubItemRect(hList,lpDraw->itemID,SubItem,LVIR_BOUNDS,&rc);	
 FillRect(hdc,&rc,hBrush); 
 ListView_GetSubItemRect(hList,lpDraw->itemID,SubItem,LVIR_LABEL,&rc);
 //アイテム情報の取得
 ListView_GetItemText(hList,lpDraw->itemID,SubItem,Text,sizeof(Text));
 //チェックボックス表示
 MoveWindow(hCheckBox,rc.left,rc.top,12,12,TRUE);
 rc.left+=12;//チェックボックスの大きさ分テキストをずらす
 rc.left+=2;//マージン
 DrawText(hdc,Text,strlen(Text),&rc,NULL);//テキスト描画
}
//ブラシの削除
DeleteObject(hBrush);
//デバイスコンテキストの復元
RestoreDC(hdc,-1);

hCheckBox:チェックボックスのハンドル

特定のアイテムだけにチェックボックスを表示させる場合はlpDraw->itemIDとSubItemなどの情報によって表示させる。
チェックボックスは表示させる数だけ必要であるが、この関数が呼び出されるたびに初期化すると、リストビューのアイテムが選択されたときに頻繁に処理が行われ、チェックボックスがちらつくため、アイテム追加時などに初期化を行い、グローバルな配列などに格納しておくのがよいだろう。

上述の方法では、チェックボックスをクリックしてもチェックがつかない。
チェックをつくようにするためには、サブクラス化を利用する。
リストの初期化時などに以下でリストビューのプロシージャをSetWindowLong関数でNewListViewProcに変更する。
OrgListViewProc = (WNDPROC) SetWindowLong(hList,GWL_WNDPROC, (LONG) NewListViewProc);
SetWindowLong関数の戻り値は元のプロシージャである。これは後で使用するためOrgListViewProcに格納しておく。
次に新しいプロシージャ(NewListViewProc)の中身を記述する。
OrgListViewProcはWNDPROC型である。
int CALLBACK NewListViewProc(HWND hWnd,UINT msg,WPARAM wp,LPARAM lp){
 switch (msg){
  case WM_COMMAND:{
   SendMessage((HWND)lp,BM_SETCHECK,!SendMessage((HWND)lp,BM_GETCHECK,0,0),0);
   NMHDR nmhdr;
   ZeroMemory(&nmhdr,sizeof(NMHDR));
   nmhdr.idFrom = LOWORD(wp);
   nmhdr.hwndFrom = hWnd;
   nmhdr.code = LVN_ITEMCHANGED;
   SendMessage(GetParent(hWnd),WM_NOTIFY,LOWORD(wp),(LPARAM)&nmhdr);
  }
  return 0;
 }
 return CallWindowProc(OrgListViewProc,hWnd,msg,wp,lp);
}
チェックボックスがクリックされるとその親であるリストビューのプロシージャにWM_COMMANDメッセージが来る。メッセージが来たら、そのハンドルのチェック状態をBM_GETCHECKメッセージを送って調べ、その状態を反転してBM_SETCHECKで送って状態を設定している。
また、チェックが変更されたときにWM_NOTIFYでLVN_ITEMCHANGEDメッセージをリストビューの親ウィンドウに通知するようにしている。
オリジナルのメッセージを設定してもよいが、今回はLVS_EX_CHECKBOXESスタイルと同じように扱えるようにするため、このようにした。

以上を含んだライブラリを公開しています。
詳しくはここをどうぞ。