ボタンとツールバーの左右に三角形のボタンが出てきてそれぞれの
コントロールを左右にスクロールすることができます。
このプログラムでは見やすくするためにわざと青と黄色に着色していますが、
地色と同じものを使うのが一般的です。
もちろん表示するスペースが十分ある時は表示されません。
というように作ります。1.コモンコントロールの準備(Comclt32.dllはVer4.71以降が必要) commctrl.hをインクルードする Comctl32.libをプロジェクトに参加させる INITCOMMONCONTROLSEX構造体のdwICCメンバにICC_PAGESCROLLER_CLASS を加える InitCommonControlsEx関数を実行 2.CreateWindowEx関数でWC_PAGESCROLLERクラスのコントロールを作る 3.ページャーコントロールに載せるコントロールを作る 4.ページャコントロールに対してPGM_SETCHILDメッセージを送る 5.PGN_CALCSIZE, PGN_SCROLL通知メッセージの処理
ページャーコントロールのスタイルには次のようなものがあります。
PGS_AUTOSCROLL | スクロールボタンにマウスをポイントするだけでスクロールします。 |
PGS_DRAGNDROP | D&Dを受け付けるウィンドウが含まれる時。 |
PGS_HORZ | 水平方向にスクロールするページャーコントロール。 |
PGS_VERT | 垂直方向にスクロールするページャーコントロール |
hwndChildはページャコントロールに載せるコントロールのハンドルです。 このメッセージを送るかわりに、Pager_SetChildマクロを使うこともできます。PGM_SETCHILD wParam = 0; lParam = (LPARAM)(HWND)hwndChild;
この通知メッセージはWM_NOTIFYの形で送られてきます。PGN_CALCSIZE lpnmcs = (LPNMPGCALCSIZE) lParam;
NMPGCALCSIZE構造体は次のように定義されています。
hdrはNMHDR構造体です。typedef struct { NMHDR hdr; DWORD dwFlag; int iWidth; int iHeight; }NMPGCALCSIZE, *LPNMPGCALCSIZE;
dwFlagはどの寸法が要求されているかを示す値です。
PGF_CALCHEIGHT | スクロールエリアの高さが要求されています。 この場合returnする前にiHeightメンバを設定しなくてはいけません。 |
PGF_CALCWIDTH | スクロールエリアの幅が要求されています。 returnする前にiWidthメンバが設定されなくてはいけません。 |
iWidthはスクロールエリアの望ましい幅を受けます。
iHeight はスクロールエリアの望ましい高さを受けます。
この通知メッセージもWM_NOTIFYの形で送られてきます。PGN_SCROLL lpnms = (LPNMPGSCROLL) lParam;
NMPGSCROLL構造体は次のように定義されています。
hdrはNMHDR構造体です。typedef struct { NMHDR hdr; BOOL fwKeys; RECT rcParent; int iDir; int iXpos; int iYpos; int iScroll; }NMPGSCROLL, *LPNMPGSCROLL;
fwKeysはスクロールが起こった時に押されているキーで、次のようなものがあります。
0ならばキーは押されていません。PGK_SHIFTはシフトキー、PGF_CONTROLはコントロールキー、
PGF_MNEUはALT(GRPH)キーが押されています。
rcParentはページャーコントロールのクライアント領域のRECTが含まれています。
iDirはスクロールが起こった時の方向を示します。PGF_SCROLLDOWN, PGF_SCROLLUP, PGF_SCROLLRIGHT, PGF_SCROLLLEFTのいずれかが来ます。
iXposはスクロール動作以前のコントロールウィンドウの水平位置が含まれます。
iYposはスクロール動作以前のコントロールウィンドウの垂直位置が含まれます。
iScrollはスクロール量を指定します。
では、プログラムを見てみましょう。 リソース・スクリプトは前章とMYBMPを取り除いた 以外全く同じなので 省略します。
このへんはいつもと同じです。// rebar04.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <commctrl.h> #include "resource.h" #define ID_BUT1 100 #define ID_COMBO 101 #define ID_TOOLBAR 102 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND MakeMyRebar(HWND); HWND MakeMyCombo(HWND); HWND MakeMyTool(HWND); void SetMyStrings(HWND); char szClassName[] = "rebar04"; //ウィンドウクラス HINSTANCE hInst; TBBUTTON tb[] = { {0, IDM_KUME, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {1, IDM_I, TBSTATE_ENABLED , TBSTYLE_BUTTON, 0, 0, 0}, {2, IDM_YASU, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {3, IDM_TAKA, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 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 = NULL; //メニュー名 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座標 320, //幅 150, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
WM_CREATEのところを見て下さい。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, no; INITCOMMONCONTROLSEX ic; static HWND hRebar, hBut, hCombo, hTool, hPager, hPagerB; REBARBANDINFO rbBand; HDC hdc; PAINTSTRUCT ps; static char szStr[256]; char szData[16]; RECT rc; LPNMHDR lpnmhdr; NMPGCALCSIZE *lpCalcSize; NMPGSCROLL *lpScroll; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES | ICC_PAGESCROLLER_CLASS; InitCommonControlsEx(&ic); hRebar = MakeMyRebar(hWnd); ZeroMemory(&rbBand, sizeof(REBARBANDINFO)); rbBand.cbSize = sizeof(REBARBANDINFO); rbBand.fMask = RBBIM_TEXT | RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE; rbBand.fStyle = RBBS_CHILDEDGE; rbBand.cxMinChild = 0; rbBand.cyMinChild = 25; hPagerB = CreateWindowEx(0, WC_PAGESCROLLER, NULL, WS_CHILD | WS_VISIBLE | PGS_HORZ, 0, 0, 0, 0, hRebar, NULL, hInst, NULL); Pager_SetBkColor(hPagerB, RGB(0, 0, 255)); hBut = CreateWindow("BUTTON", "押す", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, hPagerB, (HMENU)ID_BUT1, hInst ,NULL); SendMessage(hPagerB, PGM_SETCHILD, 0, (LPARAM)hBut); rbBand.lpText = "ボタン"; rbBand.hwndChild = hPagerB; rbBand.cx = 100; SendMessage(hRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand); hCombo = MakeMyCombo(hRebar); rbBand.lpText = "コンボボックス"; rbBand.hwndChild = hCombo; GetWindowRect(hRebar, &rc); rbBand.cx = rc.right - rc.left - 100; SendMessage(hRebar, RB_INSERTBAND,(WPARAM)-1, (LPARAM)&rbBand); hPager = CreateWindowEx(0, WC_PAGESCROLLER, NULL, WS_CHILD | WS_VISIBLE | PGS_HORZ, 0, 0, 0, 0, hRebar, NULL, hInst, NULL); Pager_SetBkColor(hPager, RGB(255, 255, 0)); hTool = MakeMyTool(hPager); SendMessage(hPager, PGM_SETCHILD, 0, (LPARAM)hTool); rbBand.fStyle |= RBBS_BREAK; //バンドの列を変える rbBand.lpText = "ツールバー"; rbBand.hwndChild = hPager; GetWindowRect(hRebar, &rc); rbBand.cx = rc.right - rc.left; SendMessage(hRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand); break; case WM_SIZE: SendMessage(hRebar, WM_SIZE, wp, lp); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetWindowRect(hRebar, &rc); TextOut(hdc, 5, rc.bottom - rc.top + 5, szStr, strlen(szStr)); EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (LOWORD(wp)) { case ID_BUT1: MessageBox(hWnd, "ボタンが押されました", "OK", MB_OK); break; case ID_COMBO: if (HIWORD(wp) == CBN_SELCHANGE) { no = SendMessage(hCombo, CB_GETCURSEL, 0, 0); SendMessage(hCombo, CB_GETLBTEXT, (WPARAM)no, (LPARAM)szData); wsprintf(szStr, "選択されているのは「%s」です", szData); InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hRebar); } break; case IDM_KUME: MessageBox(hWnd, "「粂」が押されました", "OK", MB_OK); break; case IDM_I: MessageBox(hWnd, "「井」が押されました", "OK", MB_OK); break; case IDM_YASU: MessageBox(hWnd, "「康」が押されました", "OK", MB_OK); break; case IDM_TAKA: MessageBox(hWnd, "「孝」が押されました", "OK", MB_OK); break; } break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->hwndFrom == hRebar) { if (lpnmhdr->code == RBN_HEIGHTCHANGE) { InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hRebar); } } if (lpnmhdr->hwndFrom == hPager) { switch (lpnmhdr->code) { case PGN_CALCSIZE: lpCalcSize = (LPNMPGCALCSIZE)lp; if (lpCalcSize->dwFlag == PGF_CALCWIDTH) { lpCalcSize->iWidth = 240; } break; case PGN_SCROLL: lpScroll = (LPNMPGSCROLL)lp; lpScroll->iScroll = 2; break; } } if (lpnmhdr->hwndFrom == hPagerB) { switch (lpnmhdr->code) { case PGN_CALCSIZE: lpCalcSize = (LPNMPGCALCSIZE)lp; if (lpCalcSize->dwFlag == PGF_CALCWIDTH) { lpCalcSize->iWidth = 50; } break; case PGN_SCROLL: lpScroll = (LPNMPGSCROLL)lp; lpScroll->iScroll = 2; break; } } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hBut); DestroyWindow(hCombo); DestroyWindow(hPagerB); DestroyWindow(hPager); DestroyWindow(hRebar); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
最初にレバーコントロールを作り、次に ボタンのベージャーコントロールhPagerBを作っています。 その後Pager_SetBkColorマクロでページャーコントロールの色を 指定しています。
ページャーコントロールの背景色を指定するマクロです。 戻り値は以前の背景色です。特に指定しない場合はボタンの表面色 (COLOR_BTNFACE)となります。COLORREF Pager_SetBkColor( HWND hwndPager, COLORREF clrBk );
次に、このページャーコントロールに乗せるボタンを作っています。 そして、PGM_SETCHILDメッセージでボタンをページャーコントロールの 子供に設定します。 その後、REBARBANDINFO構造体のhwndChildメンバをページャーコントロールの ハンドル(hPagerB)に指定します。親子関係に十分気をつけてください。
ツールバーを乗せるベージャーコントロールも同様に作ります。
WM_NOTIFYメッセージのところではまず、どのコントロールから来た メッセージであるかを分類して場合わけしています。
codeがPGN_CALCSIZEの場合でdwFlagがPGF_CALCWIDTHの場合は NMPGCALCSIZE構造体のiWidthメンバを設定しなくては行けません。 この値よりスクロールエリアが縮められた時スクロールボタンが出現 します。
codeがPGN_SCROLLの時NMPGSCROLL構造体のiScrollメンバを 指定します。この値が1回のスクロール量となります。
WM_CLOSEメッセージの所でプログラムを終了する時の ウィンドウを破棄する順番に気をつけてください。 なお、親ウィンドウを破棄すると子供は自動的に破棄されるので hWndだけをDestroyWindowしてもかまいません。
この2つの関数は前章と同じです。HWND MakeMyRebar(HWND hWnd) { HWND hRebar; REBARINFO rbi; hRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | RBS_BANDBORDERS | WS_CLIPCHILDREN | CCS_NODIVIDER, 0, 0, 0, 0, hWnd, NULL, hInst, NULL); ZeroMemory(&rbi, sizeof(REBARINFO)); rbi.cbSize = sizeof(REBARINFO); SendMessage(hRebar, RB_SETBARINFO, 0, (LPARAM)&rbi); return hRebar; } HWND MakeMyCombo(HWND hRebar) { HWND hCombo; hCombo = CreateWindow("COMBOBOX", "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 0, 0, 0, 120, hRebar, (HMENU)ID_COMBO, hInst, NULL); SendMessage(hCombo, CB_INSERTSTRING, -1, (LPARAM)"粂井康孝"); SendMessage(hCombo, CB_INSERTSTRING, -1, (LPARAM)"粂井櫻都"); SendMessage(hCombo, CB_INSERTSTRING, -1, (LPARAM)"粂井志麻"); SendMessage(hCombo, CB_INSERTSTRING, -1, (LPARAM)"粂井ひとみ"); SendMessage(hCombo, CB_SETCURSEL, 0, 0); return hCombo; }
ツールバーのスタイルをフラットにしました。HWND MakeMyTool(HWND hRebar) { HWND hTool; hTool = CreateToolbarEx(hRebar, //親ウィンドウ WS_CHILD | WS_VISIBLE | CCS_NODIVIDER | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST,//ウィンドウスタイル ID_TOOLBAR, //ID 4, //ボタンイメージの数 hInst, //ビットマップリソースの入っているモジュール IDR_TOOLBAR1, //ビットマップリソースのID tb, //TBBUTTON構造体のアドレス 0, //ツールバーに加えるボタンの数 0, 0, 0, 0, //ボタンの幅、高さ、イメージの幅、高さ sizeof(TBBUTTON)); //TBBUTTON構造体の大きさ SetMyStrings(hTool); SendMessage(hTool, TB_ADDBUTTONS, 4, (LPARAM)tb); return hTool; }
これは、前章と同じです。void SetMyStrings(HWND hTool) { char szBuf[64]; int iKume, iI, iYasu, iTaka; LoadString(hInst, IDS_KUME, szBuf, sizeof(szBuf) - 1); iKume = SendMessage(hTool, TB_ADDSTRING, 0, (LPARAM)szBuf); tb[0].iString = iKume; LoadString(hInst, IDS_I, szBuf, sizeof(szBuf) - 1); iI = SendMessage(hTool, TB_ADDSTRING, 0, (LPARAM)szBuf); tb[1].iString = iI; LoadString(hInst, IDS_YASU, szBuf, sizeof(szBuf) - 1); iYasu = SendMessage(hTool, TB_ADDSTRING, 0, (LPARAM)szBuf); tb[2].iString = iYasu; LoadString(hInst, IDS_TAKA, szBuf, sizeof(szBuf) - 1); iTaka = SendMessage(hTool, TB_ADDSTRING, 0, (LPARAM)szBuf); tb[3].iString = iTaka; return; }
今回はレバーコントロールの子供としてページャーコントロールを作り、 その子供としてボタンなどのコントロールを作りました。 しかし、ボタンなどの通知メッセージ(WM_COMMAND)はちゃんと 親に届いている点に注意して下さい。
Update 21/Mar/1999 By Y.Kumei