「チェックボックス付きリストビュー」の編集履歴(バックアップ)一覧はこちら
チェックボックス付きリストビュー - (2011/10/30 (日) 17:11:56) の最新版との変更点
追加された行は緑色になります。
削除された行は赤色になります。
リストビューにチェックボックスを付けるには拡張スタイルを使用する。
拡張スタイルを使用するには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 lpDrawItem = (LPDRAWITEMSTRUCT)lParam;//lParam:WM_DRAWITEMメッセージのlParam
HWND hList=lpDraw->hwndItem; //リストのハンドル
HDC hdc = lpDraw->hDC; //リストのデバイスコンテキスト
HBRUSH hBrush;//背景描画用ブラシ
LVCOLUMN LvColumn;//列項目取得用
LVITEM LvItem;//アイテム情報取得用
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);
}
//列項目情報の取得
ZeroMemory(&LvColumn,sizeof(LvColumn));
LvColumn.mask=LVCF_FMT;
ListView_GetColumn(hList,0,&LvColumn);
//アイテム情報の取得
ZeroMemory(&LvItem,sizeof(LvItem));
LvItem.iItem=lpDraw->itemID;
LvItem.iSubItem=0;
LvItem.mask=LVIF_TEXT | LVIF_IMAGE;
LvItem.pszText=Text;
LvItem.cchTextMax=sizeof(Text);
ListView_GetItem(hList,&LvItem);
//アイテムの数を取得する
int ItemNum=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_GetColumn(hList,SubItem,&LvColumn);
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スタイルと同じように扱えるようにするため、このようにした。
以上の方法を使ってライブラリを作成してみました。
ところどころ仕様が違う場所もあります。
ソースコードを同梱しているので、そちらも見ながら使ってみてください。
[[ダウンロード>www47.atwiki.jp/frostar?cmd=upload&act=open&pageid=46&file=ListViewEx.zip]]
リストビューにチェックボックスを付けるには拡張スタイルを使用する。
拡張スタイルを使用するには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スタイルと同じように扱えるようにするため、このようにした。
以上を含んだライブラリを公開しています。
詳しくは[[ここ>>ListViewEx]]をどうぞ。
表示オプション
横に並べて表示:
変化行の前後のみ表示: