「名前」または「画像」をクリックすると表示されている
写真とその名前が変わります。
また、セパレータをドラッグすると「名前」「画像」列の 大きさを変えることができます。画像はその列の幅に拡大・縮小されます。
ヘッダーコントロールを作るにはどうしたらよいのでしょうか。
で作ることができます。1.コモンコントロールの前準備 commctrl.hのインクルード、comctl32.libのリンク 2.InitCommonControlsEx関数を呼ぶ 3.ウィンドウクラスをWC_HEADERにしてCreateWindow(Ex)関数を実行 4.HDM_LAYOUTメッセージを送り、初期状態の位置、大きさを指定 5.HDM_INSERTITEMメッセージでアイテムを挿入する
HDLAYOUT構造体は、次のように定義されています。HDM_LAYOUT wParam = 0; lParam = (LPARAM) (LPHDLAYOUT) playout;
prcは、ヘッダーコントロールの座標を格納したRECT構造体へのポインタです。typedef struct _HD_LAYOUT { RECT FAR* prc; WINDOWPOS FAR* pwpos; } HDLAYOUT, FAR *LPHDLAYOUT;
pwposは、ヘッダーコントロールの位置、大きさの情報を受け取るWINDOWPOS構造体への ポインタです。
WINDOWPOS構造体は次のように定義されています。
hwndは、ウィンドウハンドルです。typedef struct _WINDOWPOS { HWND hwnd; HWND hwndInsertAfter; int x; int y; int cx; int cy; UINT flags; } WINDOWPOS;
hwndInsertAfterは、zオーダーを指定します。これについては、
DeferWindowPosの第3引数と同じです。(第218章参照)
もしくは、hwndのすぐ後ろにある
ウィンドウのハンドルを指定することもできます。
x, y, cx, cyはウィンドウの位置、大きさです。
flagsは、ウィンドウの位置や状態のオプションで、次の組み合わせで指定します。
SWP_DRAWFRAME | ウィンドウの周りにフレームを描画します |
SWP_FRAMECHANGED | ウィンドウサイズが変化していなくてもWM_NCCALCSIZEメッセージを ウィンドウに送ります。 |
SWP_HIDEWINDOW | ウィンドウを隠します。 |
SWP_NOACTIVATE | ウィンドウをアクティブ化しません。 |
SWP_NOCOPYBITS | クライアント領域のすべての内容を破棄します。 |
SWP_NOMOVE | 現在の位置にとどまります。 |
SWP_NOOWNERZORDER | オーナーウィンドウのzオーダーを変更しません。 |
SWP_NOREDRAW | 変更を再描画しません。 |
SWP_NOREPOSITION | SWP_NOOWNERZORDERと同じです。 |
SWP_NOSENDCHANGING | ウィンドウがWM_WINDOWPOSCHANGING メッセージを受け取るのを妨げます。 |
SWP_NOSIZE | 現在のサイズを保持します。 |
SWP_NOZORDER | 現在のzオーダーを保持します。 |
SWP_SHOWWINDOW | ウィンドウを表示します。 |
ヘッダーコントロールに新しいアイテムを挿入します。HDM_INSERTITEM wParam = (WPARAM) (int) index; lParam = (LPARAM) (const LPHDITEM) phdi;
indexは、挿入される新しいアイテムのインデックスを指定します。0であれば最初に挿入されます。
phdiは、HDITEM構造体のアドレスを指定します。
HDITEM構造体は、次のように定義されています。
maskは、次の組み合わせを指定します。typedef struct _HDITEM { UINT mask; int cxy; LPTSTR pszText; HBITMAP hbm; int cchTextMax; int fmt; LPARAM lParam; #if (_WIN32_IE >= 0x0300) int iImage; int iOrder; #endif #if (_WIN32_IE >= 0x0500) UINT type; LPVOID pvFilter; #endif } HDITEM, FAR * LPHDITEM;
HDI_BITMAP | hbmメンバが有効です。 |
HDI_FORMAT | fmtメンバが有効です。 |
HDI_FILTER | typeとpvFilterメンバが有効です。(Comctl32.dllがVer.5.80以降) |
HDI_HEIGHT | cxyメンバが有効です。 |
HDI_IMAGE | iImageメンバが有効です。(Ver.4.70以降) |
HDI_LPARAM | lParamメンバが有効です。 |
HDI_ORDER | iOrderメンバが有効です。(Ver.4.70以降) |
HDI_TEXT | pszText, cchTextMaxメンバが有効です。 |
HDI_WIDTH | cxyメンバが有効です。 |
cxyは、アイテムの幅、または高さです。
pszTextは、アイテムの文字列です。
hbmは、アイテムのビットマップハンドルです。
cchTextMaxは、アイテム文字列の長さです。
fmtはアイテムのフォーマットを指定します。
テキストの調整は、次の中から1つを指定します。
HDF_CENTER | 中央に配置。 |
HDF_LEFT | 左寄せ。 |
HDF_RIGHT | 右寄せ。 |
また、次の中から1つを指定します。
HDF_BITMAP | アイテムはビットマップを表示します。 | HDF_BITMAP_ON_RIGHT | ビットマップはテキストの右側に表示されます。 (Ver.4.70以降) | HDF_OWNERDRAW | ヘッダーコントロールのオーナーがアイテムを描画します。 | HDF_STRING | アイテムは文字列を表示します。 |
今までの値に、次のものを組み合わせることができます。
HDF_IMAGE | イメージリストからのイメージを表示します。(Ver.4.70以降) |
HDF_JUSTIFYMASK | 前のテーブルの3つの調整フラグに関してビットを分離します。 |
HDF_RTLREADING | ヘブライ語などのように右から左へ書く言語。 |
lParamは、アプリケーション定義のデータです。
iImageは、イメージリストの0から始まるインデックスを指定します。(Ver.4.70以降)
iOrderは、ヘッダーコントロール内のアイテムに見える順番。(Ver.4.70以降)
typeは、pvFilterによって指定されるフィルターのタイプ。(Ver5.80以降)
HDFT_ISTRING | 文字列データ |
HDFT_ISNUMBER | 数値データ |
HDFT_HASNOVALUE | pvFilterを無視 |
pvFilterは、アプリケーション定義のデータアイテム。
では、プログラムを見てみましょう。
メニューとビットマップリソースのリソース・スクリプトです。// header.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP01 BITMAP DISCARDABLE "my01.bmp" MYBMP02 BITMAP DISCARDABLE "my02.bmp" MYBMP03 BITMAP DISCARDABLE "my03.bmp"
適当なビットマップを3つ用意してリソースにしておいてください。
コモンコントロールの前準備を忘れないでください。// header01.cpp #ifndef STRICT #define STRICT #endif #define ID_HEADER 100 #include <windows.h> #include <commctrl.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MySetItem(HWND); char szClassName[] = "header01"; //ウィンドウクラス HINSTANCE hInst; char szBMP[3][16] = {"MYBMP01", "MYBMP02", "MYBMP03"}; char szBMPName[3][16] = {"cat01", "cat02", "cat03"}; int no = 0;
いつもとほとんど同じですが、インスタンスハンドルをグローバル変数にコピーしている点に 注意してください。int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるヘッダコントロール", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
メインウィンドウのプロシージャです。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HWND hHeader; INITCOMMONCONTROLSEX ic; RECT rc; HDLAYOUT hdl; WINDOWPOS wpos; HDC hdc, hdc_mem; PAINTSTRUCT ps; BITMAP bmp_info; HBITMAP hBMP; int wx, wy, nHeader; static int width_0 = 100, wxbmp = 300, fonth; NMHDR *lpnmhdr; NMHEADER *lpnh; TEXTMETRIC tm; 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); MySetItem(hHeader); hdc = GetDC(hWnd); GetTextMetrics(hdc, &tm); fonth = tm.tmHeight; ReleaseDC(hWnd, hdc); break; case WM_SIZE: rc.left = 0; rc.top = 0; rc.right = LOWORD(lp); rc.bottom = LOWORD(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); break; case WM_PAINT: GetWindowRect(hHeader, &rc); nHeader = rc.bottom - rc.top; hdc = BeginPaint(hWnd, &ps); hBMP = LoadBitmap(hInst, szBMP[no]); GetObject(hBMP, sizeof(BITMAP), &bmp_info); wx = bmp_info.bmWidth; wy = bmp_info.bmHeight; hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBMP); StretchBlt(hdc, width_0, nHeader, wxbmp, wy * wxbmp / wx, hdc_mem, 0, 0, wx, wy, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBMP); TextOut(hdc, 10 , ((wy * wxbmp / wx) - fonth) / 2 + nHeader, szBMPName[no], strlen(szBMPName[no])); EndPaint(hWnd, &ps); break; case WM_NOTIFY: lpnmhdr = (NMHDR *)lp; if (lpnmhdr->hwndFrom != hHeader) return (DefWindowProc(hWnd, msg, wp, lp)); switch (lpnmhdr->code) { case HDN_ITEMCLICK: no++; if (no > 2) no = 0; InvalidateRect(hWnd, NULL, TRUE); break; case HDN_ENDTRACK: lpnh = (NMHEADER *)lp; switch (lpnh->iItem) { case 0: width_0 = lpnh->pitem->cxy; break; case 1: wxbmp = lpnh->pitem->cxy; break; } InvalidateRect(hWnd, NULL, TRUE); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hHeader); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_CREATEメッセージが来たら、InitCommonControlsEx関数を呼びます。
次にWC_HEADERクラスでウィンドウを作ります。この時ウィンドウスタイルに HDS_BUTTONSを加えると、アイテムがボタン状になりクリックすることができるようになります。
自作関数MySetItem関数でヘッダコントロールにアイテムを挿入します。
フォントの高さを調べてstaticな変数fonthに代入しておきます。
WM_SIZEメッセージが来たら、親のクライアント領域の大きさを調べておいて、 HDM_LAYOUTメッセージをヘッダーコントロールに送り、好ましいヘッダコントロールの 位置、大きさをWINDOWPOS構造体に取得します。
WINDOWPOS構造体のデータを元に、SetWindowPos関数でヘッダーコントロールの 位置、大きさを調整します。
WM_PAINTメッセージが来たら、ヘッダコントロールの高さを求めておいて ビットマップと、テキストをクライアント領域に表示します。
この時、ビットマップの位置、大きさ、テキストの位置に注意してください。
width_0は、アイテム0の幅です。wxbmpはクライアント領域に表示された時の
ビットマップの幅です。wx, wyはオリジナルのビットマップの幅と高さです。
表示されるビットマップの幅が変更になっても幅、高さの比が変わらないようにしてあります。
WM_NOTIFYメッセージが来たら、どこのウィンドウから来たものか調べて ヘッダーコントロール以外から来たものは、DefWindowProcに任せています。
ヘッダーコントロールからの通知メッセージで、HDN_ITEMCLICKとHDN_ENDTRACK のみを処理しています。
ユーザーがヘッダーコントロールをクリックしたら、その親ウィンドウに通知されます。HDN_ITEMCLICK phdr = (LPNMHEADER) lParam;
phdrはNMHEADER構造体のアドレスです。
NMHEADER構造体は、次のように定義されています。
hdrは、通知メッセージの情報を含んだNMHDR構造体です。typedef struct tagNMHEADER{ NMHDR hdr; int iItem; int iButton; HDITEM FAR* pitem; } NMHEADER, FAR* LPNMHEADER;
iItemは、ヘッダアイテムの0から始まるインデックスです。
iButtonは、通知メッセージを生成したマウスボタンの種類を表します。
0は左ボタン、1は右ボタン、2は中ボタンです。
pitemは、HDITEM構造体へのポインタです。
ユーザーがデバイダーのドラッグを完了したとき、ヘッダーコントロールの 親に通知されます。HDN_ENDTRACK phdn = (LPNMHEADER) lParam;
phdnは、NMHEADER構造体のアドレスです。
もう一度通知メッセージの処理の所を見てみると、 HDN_ITEMCLICKが通知されると、どのアイテムであってもnoを1つ増やします。 これは、0, 1, 2の値を循環します。(WM_PAINTの処理の所を見直してください。)
HDN_ENDTRACKが通知された時、それが0番のアイテムであるときはwidth_0にその幅を
代入します。
1番のアイテムの時は、wxbmpにその幅を代入します。
そして、クライアント領域に無効領域を発生させて再描画させます。
プログラム終了時にヘッダーコントロールを破棄します。
ヘッダーコントロールにアイテムを挿入する関数です。BOOL MySetItem(HWND hHeader) { HDITEM hi; hi.mask = HDI_FORMAT | HDI_TEXT | HDI_WIDTH; hi.pszText = "名 前"; hi.cxy = 100; hi.fmt = HDF_CENTER | HDF_STRING; hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 0, (LPARAM)&hi); hi.pszText = "画 像"; hi.cxy = 300; hi.cchTextMax = strlen(hi.pszText); SendMessage(hHeader, HDM_INSERTITEM, 1, (LPARAM)&hi); return TRUE; }
今回は、ヘッダコントロールの下方に直接テキストやら、ビットマップを 描画してみました。いろいろな使い方がありそうですね。
Update 09/Oct/2000 By Y.Kumei