第190章 範囲を持つトラックバー


コモンコントロールのうちトラックバーについてはまだ、解説 していませんでした。数値や、範囲を設定するのにトラックバー を利用すると便利です。



トラックバーそのものの扱いは簡単ですが、左の図のように範囲を 指定できるようにするにはちょっとめんどうな部分があります。

今回作るトラックバーは、範囲を指定する時はコントロールキーを 押して、その後つまみをドラッグします。ボタンから指を離した点 までが範囲となります。



まず、リソースエディタで上の図のようなダイアログボックス テンプレートを作ります(トラックバーを除く)。トラックバーは このダイアログボックスのプロシージャの中で作ります。

0.一般的なコモンコントロールの準備   INITCOMMONCONTROLSEX構造体のdwICCメンバは   ICC_BAR_CLASSESとします。 1.ウィンドウクラスをTRACKBAR_CLASSとしてCreateWindowEx関数で   トラックバーを作る

たったこれだけです。では、プログラムを見てみましょう。

// track01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "ダイアログ(&D)" BEGIN MENUITEM "トラックバー・ダイアログ(&T)", IDM_TRACK END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 154, 57 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "トラックバー" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,36,50,14 PUSHBUTTON "範囲取消",IDC_CANCELRANGE,97,36,50,14 LTEXT "Static",IDC_STATICFROM,121,7,26,10 LTEXT "Static",IDC_STATICTO,121,23,26,10 LTEXT "From:",IDC_STATIC,98,7,17,8 LTEXT "To:",IDC_STATIC,98,23,10,8 END

ごく普通のメニューとダイアログボックスのリソース・スクリプトです。

// track01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <commctrl.h> #include "resource.h" #define ID_TRACK 1 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyTrackProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "track01"; //ウィンドウクラス HINSTANCE hInst; WNDPROC OrgTrackProc; //元々のトラックバーのプロシージャ HWND hFrom, hTo, hCancel;

今回は親ウィンドウ、ダイアログボックス、サブクラス化された トラックバーの3つのプロシージャがあります。

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座標 230,//幅 100,//高さ 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; INITCOMMONCONTROLSEX ic; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_BAR_CLASSES; InitCommonControlsEx(&ic); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_TRACK: DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

親ウィンドウのプロシージャも特に開設は不要ですね。メニューからIDM_TRACKが 選択されたらダイアログボックスを出すようにしています。

//ダイアログボックスのプロシージャ LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hTrack; switch (msg) { case WM_INITDIALOG: hTrack = CreateWindowEx(0,//拡張ウィンドウスタイル TRACKBAR_CLASS,//ウィンドウクラス "",//ウィンドウの名前 WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS | TBS_ENABLESELRANGE,//ウィンドウスタイル 10, 10, 150, 30,//位置、大きさ hDlg,//親ウィンドウ (HMENU)ID_TRACK,//コントロールID hInst,//インスタンスハンドル NULL); //元々のプロシージャを保存してトラックバーをサブクラス化 OrgTrackProc = (WNDPROC)GetWindowLong(hTrack, GWL_WNDPROC); SetWindowLong(hTrack, GWL_WNDPROC, (LONG)MyTrackProc); //トラックバーの範囲設定 SendMessage(hTrack, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELPARAM(0, 10)); //ダイアログボックスのコントロール類のハンドルをグローバル変数に取得 hFrom = GetDlgItem(hDlg, IDC_STATICFROM); hTo = GetDlgItem(hDlg, IDC_STATICTO); hCancel = GetDlgItem(hDlg, IDC_CANCELRANGE); //トラックバーにフォーカスを持たせる SetFocus(hTrack); SetWindowText(hFrom, ""); SetWindowText(hTo, ""); //「範囲解除」ボタンを使用不能にしておく EnableWindow(hCancel, FALSE); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: case IDCANCEL: //サブクラス化の解除 SetWindowLong(hTrack, GWL_WNDPROC, (LONG)OrgTrackProc); DestroyWindow(hTrack); EndDialog(hDlg, IDOK); return TRUE; case IDC_CANCELRANGE: SendMessage(hTrack, TBM_CLEARSEL, (WPARAM)TRUE, 0); SetWindowText(hFrom, ""); SetWindowText(hTo, ""); EnableWindow(hCancel, FALSE); SetFocus(hTrack); return TRUE; } break; default: return FALSE; } return FALSE; }

トラックバーはダイアログボックスの子供として作るので、当然トラックバー関連の ことは大部分このプロシージャでプログラムします。

WM_INITDIALOGが来たらトラックバーを作ります。この時ウィンドウスタイルに TBS_AUTOTICKSを加えると自動的にメモリが1ずつ付きます。また、TBS_ENABLESELRANGE を加えると選択範囲を表示できるトラックバーとなります。

このあと、トラックバーをサブクラス化しています。理由については後述します。

トラックバーにTBM_SETRANGEメッセージを送るとトラックバーのつまみの最小、最大の 位置を設定することができます。

TBM_SETRANGE wParam = (WPARAM) (BOOL) fRedraw; lParam = (LPARAM) MAKELONG(lMinimum, lMaximum);

fRedrawがTRUEの場合、範囲設定後つまみが再描画されます。

lMinimum, lMaximumはつまみの最小、最大位置です。

ダイアログボックスの「範囲解除」ボタン(IDC_CANCELRANGE)が押された時は トラックバーにTBM_CLEARSELメッセージを送って範囲をクリアします。

TBM_CLEARSEL wParam = (WPARAM) (BOOL) fRedraw; lParam = 0;

fRedrawがTRUEだと範囲がクリアされた後トラックバーが再描画されます。

さて、ダイアログボックスのプロシージャ内でトラックバーに対して 行っている操作はこのくらいです。では、ユーザーが範囲の選択を 開始したのはどのように知って、それを表示するのはどうすれば よいのでしょうか。最初に書いたようにこのプログラムでは コントロールキーを押すと範囲選択が開始されます。次につまみを ドラッグして離したところまでが選択範囲となります。 通知メッセージを処理すればよさそうな気がしますが、トラックバーの 通知メッセージはWM_HSCROLLのlParamとして送られてきます。 通知メッセージの種類が少なくコントロールキーが押されたかどうか知るのは 無理なようです。こういう時はサブクラス化をします。

LRESULT CALLBACK MyTrackProc(HWND hTrack, UINT msg, WPARAM wp, LPARAM lp) { static BOOL bRange; //範囲設定中かどうか DWORD dwPos; char szRange[8]; switch (msg) { case WM_KEYDOWN: if (wp == VK_CONTROL) { bRange = TRUE; dwPos = SendMessage(hTrack, TBM_GETPOS, 0, 0); SendMessage(hTrack, TBM_SETSELSTART, (WPARAM)FALSE, (LPARAM)dwPos); wsprintf(szRange, "%d", dwPos); SetWindowText(hFrom, szRange); EnableWindow(hCancel, TRUE); return 0; } break; case WM_LBUTTONUP: if (bRange) { dwPos = SendMessage(hTrack, TBM_GETPOS, 0, 0); SendMessage(hTrack, TBM_SETSELEND, (WPARAM)TRUE, (LPARAM)dwPos); wsprintf(szRange, "%d", dwPos); SetWindowText(hTo,szRange); } bRange = FALSE; break; } return (CallWindowProc(OrgTrackProc, hTrack, msg, wp, lp)); }

コントロールキーが押されたらbRangeをTRUEにして範囲選択中であることを マークしておきます。この時のつまみの位置が範囲選択開始点となります。 つまみ位置を知るにはTBM_GETPOSメッセージを使います。

TBM_GETPOS wParam = 0; lParam = 0;

戻り値がつまみの現在位置です。

TBM_SETSELSTART wParam = (WPARAM) (BOOL) fRedraw; lParam = (LPARAM) (LONG) lStart;

選択範囲の開始位置を設定します。

fRedrawはTRUEの場合選択開始位置が設定された後つまみが 再描画されます。

lStartは選択開始位置です。

さて、選択開始位置が決まったらダイアログボックスの スタティックコントロール(hFrom)にその値を表示しておきます。

さて、bRangeがTRUEの時にマウスの左ボタンが離される (WM_LBUTTONUP)ということは その場所が選択範囲の終了位置ということになります。

TBM_SETSELENDメッセージで選択範囲終了位置を指定すれば 選択範囲が描画されます。

TBM_SETSELEND wParam = (WPARAM) (BOOL) fRedraw; lParam = (LPARAM) (LONG) lEnd;

wParam, lParamの意味は特に説明不要ですね。

選択範囲の終了位置がわかったらダイアログボックスの スタティックコントロール(hTo)にその値を表示しておきます。

サブクラス化したプロシージャの中で注意すべき点は メッセージを処理した時、自分の処理だけでreturnすると本来の 機能が実行されなくなる点です。自分で処理したあと本来の機能も 実行させる為にはCallWindowProc関数にメッセージを渡す必要が あります。サブクラス化した後、そのウィンドウの挙動がおかしくなった 場合はこの点をチェックしてみて下さい。


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

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