左の図のように各ヘッダーアイテムに小さいビットマップが表示されています。
ヘッダーコントロールとは関係ありませんが、今回はメニューのチェックも
自作ビットマップにしてみました。
ヘッダコントロールにイメージリストをセットするには次のようにします。
1.イメージリストにしたいビットマップをリソースにしておく。
2.ImageList_LoadImageマクロでイメージリストを作成。
3.Header_SetImageListマクロでヘッダコントロールにイメージリストをセットします。
4.HDITEM構造体のmaskメンバにHDI_IMAGEを加えます。
5.HDITEM構造体のiImageメンバに表示したにイメージリストのインデックスを指定します。
6.ヘッダコントロールにHDM_INSERTITEMメッセージを送ります。
これで、イメージリストが表示されます。
ビットマップ、カーソル、アイコンからイメージリストを作ります。HIMAGELIST ImageList_LoadImage( HINSTANCE hi, LPCTSTR lpbmp, int cx, int cGrow, COLORREF crMask, UINT uType, UINT uFlags );
lpbmpには、ロードするイメージを指定します。
cxには、各イメージの幅を指定します。
cGrowには、イメージリストを拡張するときのイメージ数を指定します。
crMaskには、マスクする色を指定します。マスクが不要の時はCLR_NONEを指定します。
uTypeには、ロードするイメージのタイプを指定します。
ビットマップの時はIMAGE_BITMAP、カーソルの時はIMAGE_CURSOR、アイコンの時は
IMAGE_ICONを指定します。
uFlagsには、どのようにしてイメージをロードするかを指定します。
LR_DEFAULTCOLORは、ディスプレイのカラーフォーマットを使用します。
LR_LOADFROMFILEは、イメージをファイルからロードします。
他にもいくつかパラメーターがありますがヘルプで調べてみてください。
成功するとイメージリストのハンドルが返ります。失敗するとNULLが返されます。
ヘッダコントロールにイメージリストをセットするマクロです。 ヘッダコントロールにHDM_SETIMAGELISTメッセージを送っても同じことができます。HIMAGELIST Header_SetImageList( HWND hwndHD, HIMAGELIST himl );
hwndHDには、ヘッダーコントロールのハンドルを指定します。
himlには、イメージリストのハンドルを指定します。
さて、メニューのチェックマークを独自のビットマップに変更するにはどうしたらよいのでしょうか。
これには、SetMenuItemInfo関数を使います。この関数については
第181章に解説があります。
MENUITEMINFO構造体のfMaskメンバにMIIM_CHECKMARKSを加えます。 これで、hbmpCheckedやhbmpUnchecked メンバが有効となります。これにそれぞれのビットマップハンドルを 指定すればよいですね。
今回作るプログラムではあらかじめ次のようなビットマップを作っておきます。
mylist.bmpはリソースエディタのツールバーを作るときのものを流用しました。
myilist.bmp | mycheck.bmp | myuncheck.bmp |
---|---|---|
では、プログラムを見てみましょう。
メニューにIDM_HEADERIMAGEが増えました。// header07.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "新規作成(&N)...", IDM_NEW MENUITEM "開く(&O)...", IDM_OPEN MENUITEM SEPARATOR MENUITEM "名前を付けて保存(&A)...", IDM_SAVEAS MENUITEM "上書き保存(&S)", IDM_SAVE, GRAYED MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END POPUP "編集(&E)" BEGIN MENUITEM "データの追加(&A)...", IDM_ADDITEM MENUITEM "データの削除(&D)...", IDM_DELITEM MENUITEM "データ編集(&I)...", IDM_EDITITEM END POPUP "オプション(&O)" BEGIN POPUP "フォントの変更(&F)" BEGIN MENUITEM "ヘッダコントロール(&H)...", IDM_HEADERFONT MENUITEM "リストビュー(&L)...", IDM_LISTFONT END MENUITEM "ヘッダコントロールのイメージ(&I)", IDM_HEADERIMAGE END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // ADDITEM DIALOG DISCARDABLE 0, 0, 143, 123 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "データの追加" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,36,7,97,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,36,29,97,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,36,47,29,12,ES_AUTOHSCROLL | ES_NUMBER EDITTEXT IDC_EDIT4,36,63,99,33,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "OK",IDOK,7,102,50,14 PUSHBUTTON "キャンセル",IDCANCEL,86,102,50,14 LTEXT "氏名:",IDC_STATIC,7,7,16,8 LTEXT "住所:",IDC_STATIC,7,29,16,8 LTEXT "年齢:",IDC_STATIC,7,49,16,8 LTEXT "備考:",IDC_STATIC,7,65,16,8 END MYFONTDLG DIALOG DISCARDABLE 0, 0, 115, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "フォント選択" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,73,44,14 PUSHBUTTON "キャンセル",IDCANCEL,61,73,44,14 COMBOBOX 1136,7,7,73,72,CBS_SIMPLE | CBS_SORT | WS_VSCROLL | WS_TABSTOP COMBOBOX 1138,85,7,19,71,CBS_SIMPLE | CBS_SORT | WS_VSCROLL | WS_TABSTOP END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYILIST BITMAP DISCARDABLE "myilist.bmp" MYCHECK BITMAP DISCARDABLE "check.bmp" MYUNCHECK BITMAP DISCARDABLE "uncheck.bmp"
ビットマップリソースが増えました。
MySetItem関数の引数が変更となりました。(後述)// header07.cpp #ifndef STRICT #define STRICT #endif #define ID_HEADER 100 #define ID_LIST 101 #define NO_OF_SUBITEM 4 #define UP 1 #define DOWN 2 #define MAX_FILE_SIZE (1024 * 64) #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include <imm.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int CALLBACK MyCompProc(LPARAM, LPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MySetItem(HWND, HWND, BOOL); BOOL MySetListItem(HWND); LRESULT CALLBACK AddItemProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK EditItemProc(HWND, UINT, WPARAM, LPARAM); BOOL MySaveAs(HWND, HWND); BOOL MySave(HWND, HWND); BOOL MyOpen(HWND, HWND); LRESULT CALLBACK MyListProc(HWND, UINT,WPARAM, LPARAM); BOOL MyChangeFont(HWND, HFONT *); char szClassName[] = "header07"; //ウィンドウクラス HINSTANCE hInst; int sortsubno[NO_OF_SUBITEM]; HWND g_hList; int no_of_item, param_of_item; int no_of_edititem; //編集するアイテム BOOL bChanged; //内容が変更されたかどうか static char szFile[MAX_PATH], szTitle[MAX_PATH]; char szWinTitle[MAX_PATH], *szOrgTitle = "猫でもわかるヘッダコントロール [%s]"; WNDPROC OrgListProc; //元々のリストビューのプロシージャ HIMAGELIST hImage; BOOL bHeaderImage = TRUE; HBITMAP hCheck, hUncheck;
WinMain, InitApp, InitInstanceの各関数に変更はありません。
親ウィンドウのプロシージャです。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, nHeaderh, nItem, i; static HWND hHeader, hList, hEdit; INITCOMMONCONTROLSEX ic; RECT rc; HDLAYOUT hdl; WINDOWPOS wpos; NMHDR *lpnmhdr; NMHEADER *lpnh; char szBuf[256], szName[64]; DWORD dwExStyle; HMENU hMenu; SCROLLINFO si; static HFONT hHeaderF, hListF; MENUITEMINFO mi; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&ic); hHeader = CreateWindow(WC_HEADER, "", WS_CHILD | WS_BORDER | HDS_BUTTONS, 0, 0, 0, 0, hWnd, (HMENU)ID_HEADER, hInst, NULL); hList = CreateWindow(WC_LISTVIEW, "", WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER | LVS_EDITLABELS, 0, 0, 0, 0, hWnd, (HMENU)ID_LIST, hInst, NULL); //元々のリストビューのプロシージャを保存 OrgListProc = (WNDPROC)GetWindowLong(hList, GWL_WNDPROC); //リストビューのサブクラス化 SetWindowLong(hList, GWL_WNDPROC, (LONG)MyListProc); SetWindowLong(hList, GWL_USERDATA, (LONG)hHeader); dwExStyle = ListView_GetExtendedListViewStyle(hList); dwExStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES; ListView_SetExtendedListViewStyle(hList, dwExStyle); g_hList = hList; MySetItem(hHeader, hList, TRUE); MySetListItem(hList); hCheck = LoadBitmap(hInst, "MYCHECK"); hUncheck = LoadBitmap(hInst, "MYUNCHECK"); break; case WM_INITMENU: hMenu = GetMenu(hWnd); if (bChanged && strcmp(szTitle, "") != 0) EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED); else EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED); memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE | MIIM_CHECKMARKS; mi.hbmpChecked = hCheck; mi.hbmpUnchecked = hUncheck; if (bHeaderImage) { mi.fState = MFS_UNCHECKED; } else { mi.fState = MFS_CHECKED; } SetMenuItemInfo(hMenu, IDM_HEADERIMAGE, FALSE, &mi); DrawMenuBar(hWnd); break; case WM_SIZE: rc.left = 0; rc.top = 0; rc.right = LOWORD(lp); rc.bottom = HIWORD(lp); hdl.pwpos = &wpos; hdl.prc = &rc; SendMessage(hHeader, HDM_LAYOUT, 0, (LPARAM)&hdl); SetWindowPos(hHeader, wpos.hwndInsertAfter, wpos.x, wpos.y, wpos.cx, wpos.cy, wpos.flags | SWP_SHOWWINDOW); GetWindowRect(hHeader, &rc); nHeaderh = rc.bottom - rc.top; MoveWindow(hList, 0, nHeaderh, LOWORD(lp), HIWORD(lp) - nHeaderh, TRUE); memset(&si, 0, sizeof(SCROLLINFO)); si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_POS; GetScrollInfo(hList, SB_HORZ, &si); ListView_Scroll(hList, -si.nPos, 0); break; case WM_NOTIFY: lpnmhdr = (NMHDR *)lp; if (lpnmhdr->hwndFrom == hHeader) { switch (lpnmhdr->code) { case HDN_ITEMCLICK: lpnh = (NMHEADER *)lp; if (sortsubno[lpnh->iItem] == UP) sortsubno[lpnh->iItem] = DOWN; else sortsubno[lpnh->iItem] = UP; ListView_SortItems(hList, MyCompProc, lpnh->iItem); bChanged = TRUE; break; case HDN_ENDTRACK: lpnh = (NMHEADER *)lp; ListView_SetColumnWidth(hList, lpnh->iItem, lpnh->pitem->cxy); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_ADDITEM: no_of_item = ListView_GetItemCount(hList); if (DialogBox(hInst, "ADDITEM", hWnd, (DLGPROC)AddItemProc) == IDOK) bChanged = TRUE; param_of_item++; break; case IDM_DELITEM: while (1) { nItem = ListView_GetNextItem(hList, -1, LVNI_ALL | LVNI_SELECTED); if (nItem == -1) break; ListView_GetItemText(hList, nItem, 0, szName, sizeof(szName)); wsprintf(szBuf, "「%s」の項目を削除してよろしいですか", szName); id = MessageBox(hWnd, szBuf, "項目の削除", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { ListView_DeleteItem(hList, nItem); bChanged = TRUE; } else //削除しない場合は選択状態を解除しないとまずい(いつまでも聞かれる) ListView_SetItemState(hList, nItem, 0, LVIS_SELECTED); } break; case IDM_EDITITEM: while (1) { nItem = ListView_GetNextItem(hList, -1, LVNI_ALL | LVNI_SELECTED); if (nItem == -1) break; no_of_edititem = nItem; //編集するアイテムNOをプロシージャからもわかるようにする if (DialogBox(hInst, "ADDITEM", hWnd, (DLGPROC)EditItemProc) == IDOK) bChanged = TRUE; } break; case IDM_SAVEAS: MySaveAs(hWnd, hList); wsprintf(szWinTitle, szOrgTitle, szTitle); SetWindowText(hWnd, szWinTitle); break; case IDM_SAVE: MySave(hWnd, hList); break; case IDM_NEW: if (bChanged == TRUE) { id = MessageBox(hWnd, "現在のデータに変更があります。破棄してもよろしいですか", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; } ListView_DeleteAllItems(hList); wsprintf(szWinTitle, szOrgTitle, ""); SetWindowText(hWnd, szWinTitle); break; case IDM_OPEN: if (bChanged == TRUE) { id = MessageBox(hWnd, "現在のデータに変更があります。破棄してもよろしいですか", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; } ListView_DeleteAllItems(hList); wsprintf(szWinTitle, szOrgTitle, ""); SetWindowText(hWnd, szWinTitle); if (MyOpen(hWnd, hList)) { wsprintf(szWinTitle, szOrgTitle, szTitle); SetWindowText(hWnd, szWinTitle); } break; case IDM_HEADERFONT: MyChangeFont(hHeader, &hHeaderF); break; case IDM_LISTFONT: MyChangeFont(hList, &hListF); break; case IDM_HEADERIMAGE: for (i = 0; i <= 3; i++) Header_DeleteItem(hHeader, 0); if (bHeaderImage) { ImageList_Destroy(hImage); hImage = NULL; bHeaderImage = FALSE; MySetItem(hHeader, hList, FALSE); } else { bHeaderImage = TRUE; MySetItem(hHeader, hList, FALSE); } break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_CLOSE: if (bChanged == TRUE) { id = MessageBox(hWnd, "データに変更があります。破棄してもよろしいですか", "注意", MB_YESNO | MB_ICONQUESTION); if (id == IDNO) break; } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hHeaderF) DeleteObject(hHeaderF); if (hListF) DeleteObject(hListF); DeleteObject(hCheck); DeleteObject(hUncheck); //サブクラス化の解除 SetWindowLong(hList, GWL_WNDPROC, (LONG)OrgListProc); if (hImage) { if (ImageList_Destroy(hImage) != 0) MessageBox(hWnd, "イメージリスト破棄成功", "OK", MB_OK); else MessageBox(hWnd, "イメージリスト破棄失敗", "Error", MB_OK); } DestroyWindow(hList); DestroyWindow(hHeader); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_CREATEメッセージが来たときに、"MYCHECK"と"MYUNCHECK"をロードしてビットマップハンドルを 取得しておきます。
また、WM_CREATEメッセージが来て初めてMySetItem関数を呼ぶときは最後の引数をTRUEにします。 2番目の引数は使われないので、何でもよいです。
WM_INITMENUメッセージが来たときに、bHeaderImageがTRUEならチェックマークを"MYUNCHECK"に FALSEなら"MYCHECK"にセットします。
メニューからIDM_HEADERIMAGEが選択されたら、まずヘッダーコントロールのアイテムをすべて破棄します。 bHeaderImageがTRUEなら、イメージリストのハンドルを破棄してbHeadImageをFALSEにします。 そして、MySetItem関数を呼んでヘッダコントロールにアイテムをセットし直します。この時 MySetItem関数の2番目の引数はリストビューのハンドル、3番目の引数はFALSEにします。 初回呼び出しの時のみヘッダコントロールの各アイテムの幅をこの関数で決めますが、 それ以外の時はリストビューの幅に合わせる必要があるからです。
bHeaderImageがFALSEなら、bHeaderImageをTRUEにしてMySetItem関数を呼びます。
プログラム終了時にイメージリストが有効なら破棄します。ビットマップハンドルも破棄します。
ヘッダコントロールにアイテムをセットする関数です。BOOL MySetItem(HWND hHeader, HWND hList, BOOL bInitial) { HDITEM hi; if (bHeaderImage) { hImage = ImageList_LoadImage(hInst, "MYILIST", 16, 0, CLR_NONE, IMAGE_BITMAP, LR_DEFAULTCOLOR); if (hImage == NULL) MessageBox(NULL, "Error! ImageList_LoadImage", "Error", MB_OK); Header_SetImageList(hHeader, hImage); hi.mask = HDI_FORMAT | HDI_TEXT | HDI_WIDTH | HDI_IMAGE; } else { hi.mask = HDI_FORMAT | HDI_TEXT | HDI_WIDTH; } hi.pszText = "名 前"; hi.iImage = 0; if (bInitial) hi.cxy = 100; else hi.cxy = ListView_GetColumnWidth(hList, 0); hi.fmt = HDF_CENTER | HDF_STRING; hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 0, (LPARAM)&hi); hi.pszText = "住所"; hi.iImage = 1; if (bInitial) hi.cxy = 200; else hi.cxy = ListView_GetColumnWidth(hList, 1); hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 1, (LPARAM)&hi); hi.pszText = "年齢"; hi.iImage = 2; if (bInitial) hi.cxy = 60; else hi.cxy = ListView_GetColumnWidth(hList, 2); hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 2, (LPARAM)&hi); hi.pszText = "備 考"; hi.iImage = 3; if (bInitial) hi.cxy = 260; else hi.cxy = ListView_GetColumnWidth(hList, 3); hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 3, (LPARAM)&hi); return TRUE; }
bHeaderImageがTRUEなら、イメージリストをセットしてFALSEならセットしません。 初回呼び出し(bInitialがTRUE)ならヘッダコントロールの各アイテムの幅を初期値に設定し、 そうでないときはListView_GetColumnWidthマクロでリストビューの幅に合わせます。
リストビューのカラムの幅を取得するマクロです。int ListView_GetColumnWidth( HWND hwnd, int iCol );
hwndには、リストビューのハンドルを指定します。
iColはカラムの0から始まるインデックスを指定します。
リストビューにLVM_GETCOLUMNWIDTHメッセージを送っても同じことができます。
イメージリストを扱うときは、ちょっとしたことでうまく表示されず バグ探しに時間がかかることがあります。また、一度何かのプログラムで 使っても時間がたつと取り扱い方法を忘れやすいので(筆者だけか?)時々 復習しておきましょう。MySetListItem, MyCompProc, AddItemProc, EditItemProc, MySaveAs, MySave, MyOpen, MyListProc, MyChangeFontの各関数に変更はありません。
Update 18/Dec/2000 By Y.Kumei