第290章 ヘッダーコントロール


ヘッダコントロールはリストビューの構成要素の一部です。 通常、他のコモンコントロールなどと組み合わせて使います。 今回は、単独で使い、親ウィンドウのクライアント領域に内容を 直接描画します。



「名前」または「画像」をクリックすると表示されている 写真とその名前が変わります。

また、セパレータをドラッグすると「名前」「画像」列の 大きさを変えることができます。画像はその列の幅に拡大・縮小されます。



ヘッダーコントロールを作るにはどうしたらよいのでしょうか。

1.コモンコントロールの前準備   commctrl.hのインクルード、comctl32.libのリンク 2.InitCommonControlsEx関数を呼ぶ 3.ウィンドウクラスをWC_HEADERにしてCreateWindow(Ex)関数を実行 4.HDM_LAYOUTメッセージを送り、初期状態の位置、大きさを指定 5.HDM_INSERTITEMメッセージでアイテムを挿入する

で作ることができます。

HDM_LAYOUT wParam = 0; lParam = (LPARAM) (LPHDLAYOUT) playout;

HDLAYOUT構造体は、次のように定義されています。

typedef struct _HD_LAYOUT { RECT FAR* prc; WINDOWPOS FAR* pwpos; } HDLAYOUT, FAR *LPHDLAYOUT;

prcは、ヘッダーコントロールの座標を格納したRECT構造体へのポインタです。

pwposは、ヘッダーコントロールの位置、大きさの情報を受け取るWINDOWPOS構造体への ポインタです。

WINDOWPOS構造体は次のように定義されています。

typedef struct _WINDOWPOS { HWND hwnd; HWND hwndInsertAfter; int x; int y; int cx; int cy; UINT flags; } WINDOWPOS;

hwndは、ウィンドウハンドルです。

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_NOREPOSITIONSWP_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構造体は、次のように定義されています。

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;

maskは、次の組み合わせを指定します。

HDI_BITMAPhbmメンバが有効です。
HDI_FORMATfmtメンバが有効です。
HDI_FILTERtypeとpvFilterメンバが有効です。(Comctl32.dllがVer.5.80以降)
HDI_HEIGHTcxyメンバが有効です。
HDI_IMAGEiImageメンバが有効です。(Ver.4.70以降)
HDI_LPARAMlParamメンバが有効です。
HDI_ORDERiOrderメンバが有効です。(Ver.4.70以降)
HDI_TEXTpszText, cchTextMaxメンバが有効です。
HDI_WIDTHcxyメンバが有効です。

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_HASNOVALUEpvFilterを無視

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構造体は、次のように定義されています。

typedef struct tagNMHEADER{ NMHDR hdr; int iItem; int iButton; HDITEM FAR* pitem; } NMHEADER, FAR* LPNMHEADER;

hdrは、通知メッセージの情報を含んだNMHDR構造体です。

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; }

ヘッダーコントロールにアイテムを挿入する関数です。

今回は、ヘッダコントロールの下方に直接テキストやら、ビットマップを 描画してみました。いろいろな使い方がありそうですね。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 09/Oct/2000 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。