第189章 ドロップダウンメニューを持つツールバー


IE4.Xの「戻る」「進む」ボタンの横に小さな三角形のボタンが見られます。 このボタンを押すと以前に見たページの一覧がメニューとして出てきます。 また、ボタンそのものを押すと1つ前、1つ先のページに進むこともできます。 このようなツールバーのボタンはどのように作ればよいのでしょうか。



左の図は前章で作ったものを改良したプログラムです。 「丸」と「四角」のボタンの横に小さな三角形のボタンがあり これを押すとドロップダウンメニューが表示されます。

このようなツールバーを作るにはComctl32.dllのバージョンが4.71以降で あることが必要です。

0.表示するメニューリソースを作っておく。 1.TBBUTTON構造体のfsStyleメンバをTBSTYLE_DROPDOWNに設定する。 2.ツールバーを作る 3.ツールバーに対してTB_SETEXTENDEDSTYLEメッセージを送る。   この時lParamはTBSTYLE_EX_DRAWDDARROWSにする。 4.TBN_DROPDOWN通知メッセージの処理。

と、まあこんな具合に作ります。メニューリソースは第1階層は表示されません (ダミーとなります)。第44章を参照して下さい。

TBBUTTON構造体のfStyleメンバはTBSTYLE_BUTTON | TBSTYLE_DROPDOWNとしても 単にTBSTYLE_DROPDOWNとしても同じ事です。(TBSTYLE_BUTTON=0)

CreateToolbarEx関数の第2引数にTB_EX_DRAWDDARROWSを加えてもうまくいきません。 TB_SETEXTENDEDSTYLEメッセージを使ってください。

TB_SETEXTENDEDSTYLE wParam = 0; lParam = (LPARAM)(DWORD)dwExStyle;

dwExStyleに拡張スタイルを設定します。

TBN_DROPDOWNメッセージはWM_NOTIFYメッセージの形でやってきます。

TBN_DROPDOWN lpnmtb = (LPNMTOOLBAR) lParam;

lpnmtbはNMTOOLBAR構造体へのポインタです。

typedef struct tagNMTOOLBAR { NMHDR hdr; int iItem; TBBUTTON tbButton; int cchText; LPTSTR pszText; } NMTOOLBAR, FAR* LPNMTOOLBAR;

hdrはNMHDR構造体です。

iItemはボタンのコマンドIDです。

tbButtonはTBBUTTON構造体です。

cchTextはボタンテキストの文字数です。

pszTextはボタンテキストを含んでいるバッファのアドレスです。

さて、WM_NOTIFYメッセージが来たら、これがツールバーからのもので なおかつコードがTBN_DROPDOWNであればlParamの値がこの構造体への アドレスとなっています。次にツールバーに対してTB_GETRECTメッセージを 送ります。

TB_GETRECT wParam = (WPARAM)(INT) iID; lParam = (LPARAM)(LPRECT) lprc;

このメッセージは指定されたツールバーボタンに結合した四角形を取得します。 (ボタンの4隅がわかる)

iIDはボタンのIDです。

lprcは四角形の情報を受け取るRECT構造体のアドレスです。

ボタンの座標がわかれば第44章の方法でTrackPopupMenu関数を 呼んでもよいわけですが、ここではTrackPopupMenuEx関数を使ってボタンが ポップアップメニューに隠されないようにします。 (何とかEx関数とか、何とかEX構造体というのが多いですね)

BOOL TrackPopupMenuEx( HMENU hmenu, UINT fuFlags, int x, int y, HWND hwnd, LPTPMPARAMS lptpm );

hmenuは表示するメニューのハンドルです。(GetSubMenuで取得)

fuFlagsは位置などのオプションです。
TPM_HORIZONTALはメニューがlptpmで指定した長方形に重なってしまう場合 水平方向に移動します。
TPM_VERTICALは垂直方向に移動します。
その他TrackPopupMenu関数のフラグも有効です。

x, yはメニューの位置をスクリーン座標で指定します。

hwndはメニューを所有するウィンドウハンドルを指定します。このウィンドウが WM_COMMANDメッセージを受け取ります。

lptpmはTPMPARAMS構造体へのポインタです。

typedef struct tagTPMPARAMS { UINT cbSize; RECT rcExclude; } TPMPARAMS, FAR *LPTPMPARAMS;

cbSizeはこの構造体の大きさです。

rcExcludeは重なっては困る四角形のRECT構造体です。

さて、前置きがすっかり長くなってしまいました。プログラムを 見てみましょう。

// newtool2.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Toolbar // IDR_TOOLBAR2 TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON IDM_MARU BUTTON IDM_SANKAKU BUTTON IDM_SHIKAKU END IDR_TOOLBAR1 TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON IDM_MARU BUTTON IDM_SANKAKU BUTTON IDM_SHIKAKU END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // IDR_TOOLBAR2 BITMAP DISCARDABLE "toolbar2.bmp" IDR_TOOLBAR1 BITMAP DISCARDABLE "toolbar1.bmp" ///////////////////////////////////////////////////////////////////////////// // // Menu // MARUMENU MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "丸−項目&1", IDM_RND1 MENUITEM "丸−項目&2", IDM_RND2 MENUITEM "丸−項目&3", IDM_RND3 END END SHIKAKUMENU MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "四角−項目&1", IDM_RCT1 MENUITEM "四角−項目&2", IDM_RCT2 END END

メニューの第1階層は表示されない点に注意して下さい。

// newtool2.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <commctrl.h> #include "resource.h" #define ID_TOOL 1 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND CreateMyToolbar(HWND); char szClassName[] = "newtool2"; //ウィンドウクラス HINSTANCE hInst; HIMAGELIST hImage; TBBUTTON tb[] = { {0, IDM_MARU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN, 0, 0, 0}, {1, IDM_SANKAKU, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0}, {2, IDM_SHIKAKU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN, 0, 0, 0} };

ドロップダウンメニューをつけたいボタンのスタイルをTBSTYLE_DROPDOWNにします。

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座標 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 hTool; INITCOMMONCONTROLSEX ic; LPNMHDR lpnmhdr; HMENU hMenu, hPopup; RECT rc; TPMPARAMS tpm; LPNMTOOLBAR lpnmTB; char szMenuName[32]; switch (msg) { case WM_CREATE: InitCommonControls(); ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_BAR_CLASSES; InitCommonControlsEx(&ic); hTool = CreateMyToolbar(hWnd); break; case WM_SIZE: SendMessage(hTool, WM_SIZE, wp, lp); break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->hwndFrom == hTool && lpnmhdr->code == TBN_DROPDOWN) { lpnmTB = (LPNMTOOLBAR)lp; SendMessage(hTool, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc); MapWindowPoints(hTool, HWND_DESKTOP, (LPPOINT)&rc, 2); tpm.cbSize = sizeof(TPMPARAMS); tpm.rcExclude.top = rc.top; tpm.rcExclude.left = rc.left; tpm.rcExclude.bottom = rc.bottom; tpm.rcExclude.right = rc.right; switch (lpnmTB->iItem) { case IDM_MARU: lstrcpy(szMenuName, "MARUMENU"); break; case IDM_SHIKAKU: lstrcpy(szMenuName, "SHIKAKUMENU"); break; } hMenu = LoadMenu(hInst, szMenuName); hPopup = GetSubMenu(hMenu, 0); TrackPopupMenuEx(hPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL, rc.left, rc.bottom, hWnd, &tpm); DestroyMenu(hMenu); } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_MARU: MessageBox(hWnd, "「丸」が押されました", "丸", MB_OK); break; case IDM_SANKAKU: MessageBox(hWnd, "「三角」が押されました", "三角", MB_OK); break; case IDM_SHIKAKU: MessageBox(hWnd, "「四角」が押されました", "四角", MB_OK); break; case IDM_RND1: MessageBox(hWnd, "丸メニュー項目1が選択されました", "メニュー選択", MB_OK); break; case IDM_RND2: MessageBox(hWnd, "丸メニュー項目2が選択されました", "メニュー選択", MB_OK); break; case IDM_RND3: MessageBox(hWnd, "丸メニュー項目3が選択されました", "メニュー選択", MB_OK); break; case IDM_RCT1: MessageBox(hWnd, "四角メニュー項目1が選択されました", "メニュー選択", MB_OK); break; case IDM_RCT2: MessageBox(hWnd, "四角メニュー項目2が選択されました", "メニュー選択", MB_OK); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { ImageList_Destroy(hImage); DestroyWindow(hTool); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

WM_NOTIFYメッセージの処理のし方に注意して下さい。
TrackPopupMenuEx関数ではスクリーン座標系を使うので MapWindowPoints関数を使って変換しています。

int MapWindowPoints( HWND hWndFrom, HWND hWndTo, LPPOINT lpPoints, UINT cPoints );

あるウィンドウを基準とする座標を他のウィンドウを基準とする座標に 変換します。複数の点を一度に指定することができます。

hWndFromは変換前の座標の基準となるウィンドウのハンドルです。

hWndToは変換後に基準となるウインドウのハンドルです。

lpPointsは変換する座標が入ったPOINT構造体へのポインタです。

cPointsは変換する点の個数です。

TrackPopupMenuEx関数のあとですぐにDestroyMenu関数を 呼んでいるのを不思議に思われる方がいるかもしれませんが、 これでよいと思われます。なぜなら TrackPopupMenuEx関数はユーザーがメニュー項目を選択し終わるまで 制御を返しませんのでDestroyMenu関数が呼ばれた時は すでに選択が終了してメニューは消えているからです。

HWND CreateMyToolbar(HWND hWnd) { HWND hTool; LONG lStyle; hTool = CreateToolbarEx(hWnd, //親ウィンドウ WS_CHILD | WS_VISIBLE | CCS_NODIVIDER, //ウィンドウスタイル ID_TOOL, //コントロールID 3, //ボタンイメージの数 hInst, //インスタンスハンドル IDR_TOOLBAR1, //リソースID tb, //TBBUTTON構造体のアドレス 3, //ボタンの数 0, 0, //ボタンの幅、高さ 0, 0, //ボタンイメージの幅、高さ sizeof(TBBUTTON)); //TBBUTTON構造体の大きさ lStyle = GetWindowLong(hTool, GWL_STYLE); lStyle |= TBSTYLE_FLAT; SetWindowLong(hTool, GWL_STYLE, lStyle); hImage = ImageList_LoadBitmap(hInst, MAKEINTRESOURCE(IDR_TOOLBAR2), 16, 0, RGB(255,255,255)); SendMessage(hTool, TB_SETHOTIMAGELIST, 0, (LPARAM)hImage); SendMessage(hTool, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_DRAWDDARROWS); return hTool; }

最初のほうにも書きましたが、CreateToolbarEx関数の第2引数に TBSTYLE_EX_DRAWDDARROWSを加えても無効です。 案外こういう間違いがわかりにくいバグとなるので注意して下さい。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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