レバーコントロール(クールバー)

今回はレバーコントロール(クールバー、リバーコントロール)をつけます。
レバーコントロール上にボタンをのせます。

レバーコントロール1

レバーコントロール間のサイズは、マウスのドラッグ&ドロップでサイズ変更ができます。
レバーコントロール2

また、マウスのドラッグ&ドロップで、レバーコントロールを2行にもできます。
ボタンの上では、マウスのドラッグ&ドロップができません。レバーコントロールの左側の縁でできます。
レバーコントロール3

メニュー - インコのWindowsSDK で作成したソースを修正します。

流れ

1.Windows XPに対応することを示す#define _WIN32_WINNT 0x0501 を追加
0x0501はWindows XPを示します。

#define _WIN32_WINNT version

上記のversionは、そのWindowsのバージョンまでに追加された機能を使用します。

version Windowsのバージョン
0x0500 Windows 2000
0x0501 Windows XP
0x0502 Windows Server 2003
0x0600 Windows Vista / Windows Server 2008
0x0601 Windows 7 ※1
0x0602 Windows 8 ※2
0x0603 Windows 8.1

※1 Visual C++ 2008の場合は、Windows 7で追加した機能を使えないため、versionに0x0601を指定しても意味がありません。
※2 Visual C++ 2010/2008の場合は、Windows 8で追加した機能を使えないため、versionに0x0602を指定しても意味がありません。

● _WIN32_WINNTを指定しない場合の動作

_WIN32_WINNTを定義しないと、Windows XP、Windows 2000では、レバーコントロールが表示できません。
これは、後で紹介するREBARBANDINFO構造体のメンバが、Windows Vistaで追加されたためです。

● _WIN32_WINNTを定義しない別の方法(非推奨)

_WIN32_WINNTを定義せず、REBARBANDINFO構造体のcbSizeメンバに、直接80を入れてもWindows XP、Vistaともにレバーコントロールが表示できますが、 _WIN32_WINNTを定義しない場合の、構造体のサイズは100なので、意図しない動作が起こる可能性があるため、やめておきましょう。
_WIN32_WINNTを定義せず、REBARBANDINFO構造体のcbSizeメンバに、直接80を入れた場合、誤って追加されたREBARBANDINFO構造体のメンバにセットしてもコンパイルエラーにはなりません。

● _WIN32_WINNTを指定しない場合のデフォルト値

Visual C++ のバージョン _WIN32_WINNT のデフォルト値
2013 0x0603
2012 0x0602
2010 0x0601
2008 0x0600

● _WIN32_WINNTを定義する場所

#include <windows.h> の前に定義してください。
後に定義すると以下のようなwarningが出ますが、動作上の問題はありません。
warning C4005: '_WIN32_WINNT' : マクロが再定義されました。
c:\program files\microsoft sdks\windows\v7.0a\include\sdkddkver.h(197) : '_WIN32_WINNT' の前の定義を確認してください

2.commctrl.h をインクルード

3.comctl32.lib を読み込む

WM_CREATE

WM_CREATEは、ウインドウが生成されたときに1回だけ実行されます。

1.特定のコモンコントロールクラスを登録するINITCOMMONCONTROLSEX構造体にセット
今回はレバーコントロール[ICC_COOL_CLASSES]をセットします。

typedef struct tagINITCOMMONCONTROLSEX {
    DWORD  dwSize;     // 構造体のサイズ
    DWORD  dwICC;      // ロードするクラス
} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

構造体のメンバー dwICC の定数 ロードされるコントロールクラス
ICC_LISTVIEW_CLASSES リストビュー、ヘッダーコントロール
ICC_TREEVIEW_CLASSES ツリービュー、ツールチップ
ICC_BAR_CLASSES ツールバー、ステータスバー、トラックバー、ツールチップ
ICC_TAB_CLASSES タブコントロール、ツールチップ
ICC_UPDOWN_CLASS アップダウンコントロール
ICC_PROGRESS_CLASS プログレスバー
ICC_HOTKEY_CLASS ホットキーコントロール
ICC_ANIMATE_CLASS アニメートコントロール
ICC_WIN95_CLASSES 以上のすべてのコントロール
ICC_DATE_CLASSES DTP コントロール
ICC_USEREX_CLASSES 拡張コンボボックス
ICC_COOL_CLASSES レバーコントロール
ICC_INTERNET_CLASSES IP アドレスコントロール
ICC_PAGESCROLLER_CLASS ページャーコントロール

2.InitCommonControlsEx()で、コントロールを初期化。
InitCommonControls()でもかまいません。 この場合、INITCOMMONCONTROLSEX構造体にセットする必要はありません。

BOOL InitCommonControlsEx(
    LPINITCOMMONCONTROLSEX  pInitCtrls // INITCOMMONCONTROLSEX構造体のアドレス
);

3.CreateWindowEx()で、レバーコントロールのウインドウを生成する。
ここでは、とりあえず、サイズが0×0のウインドウを生成します。

ウインドウスタイルは、コモンコントロールのスタイルも使えます。

CreateWindowEx()の第11引数hInstanceには、アプリケーションインスタンスのハンドルを入れますが、GetWindowLong()を使ってインスタンスのハンドルを取得します。

4.レバーコントロールのスタイルを決めるREBARBANDINFO構造体をセット。

typedef struct tagREBARBANDINFO{
    UINT     cbSize;    // 構造体のサイズ(80または100)
    UINT     fMask;     // どのメンバを使用するかを指定
    UINT     fStyle;    // スタイル
    COLORREF clrFore;
    COLORREF clrBack;
    LPTSTR   lpText;    // コントロールの左に挿入する文字列
    UINT     cch;
    int      iImage;
    HWND     hwndChild; // コントロールに入れるコントロール(子ウインドウ)のハンドル
    UINT     cxMinChild; // 横幅の最小値
    UINT     cyMinChild; // 高さの最小値
    UINT     cx;        // 横幅の初期値
    HBITMAP  hbmBack;   // 背景用ビットマップのハンドル
    UINT     wID;       // コントロールのID
    UINT     cyChild;  
    UINT     cyMaxChild;
    UINT     cyIntegral;
    UINT     cxIdeal;
    LPARAM   lParam;
    UINT     cxHeader;
#if (_WIN32_WINNT >= 0x0600)  // Windows Vista以上
    RECT     rcChevronLocation;  
    UINT     uChevronState; 
#endif
 } REBARBANDINFO, *LPREBARBANDINFO;

構造体のメンバー fMask の定数 有効のメンバ
RBBIM_STYLE fStyleが有効
RBBIM_COLORS clrFore,clrBackが有効
RBBIM_TEXT lpText,cchが有効
RBBIM_IMAGE iImageが有効
RBBIM_CHILD hwndChildが有効
RBBIM_CHILDSIZE cxMinChild,cyMinChildが有効
RBBIM_SIZE cxが有効
RBBIM_BACKGROUND hbmBackが有効
RBBIM_ID wIDが有効
RBBIM_IDEALSIZE cxIdealが有効
RBBIM_LPARAM lParamが有効
RBBIM_HEADERSIZE cxHeaderが有効

構造体のメンバー fStyle の定数 スタイル
RBBS_BREAK バンドの列を変える
RBBS_FIXEDSIZE バンドのサイズを固定
RBBS_CHILDEDGE 子ウィンドウの周りに隙間を入れます
RBBS_HIDDEN バンドを表示しません
RBBS_NOVERT レバーコントロールがCCS_VERTスタイルを使った時、バンドは表示されません
RBBS_FIXEDBMP バンドがリサイズしてもビットマップを移動しません
RBBS_VARIABLEHEIGHT バンドの高さを可変にします
RBBS_GRIPPERALWAYS サイズグリップ(取っ手の部分)を常に表示する(通常はバンドの数が1個のときは表示されない)
RBBS_NOGRIPPER サイズグリップを表示しない

5.コントロールに入れるコントロール(子ウインドウ)に、ボタンのハンドルを入れる。

CreateWindowEx()でボタンを作成

6.RB_INSERTBANDメッセージで、レバーコントロールのバンドを挿入

lResult = SendMessage(
    (HWND) hRebar,      // ウィンドウハンドル
    (UINT) RB_INSERTBAND,
    (WPARAM) wParam,    // バンドが挿入される位置、-1を指定すると最後に挿入
    (LPARAM) lParam     // REBARBANDINFO構造体へのアドレス
);

7.GetClientRect()で、ウィンドウのクライアント領域の座標を取得
クライアント領域とは、タイトルバー、メニューバー、 ツールバー、ステータスバー、ウィンドウ枠、スクロールバーを除いた領域のこと。

BOOL GetClientRect(
    HWND   hWnd,    // ウィンドウハンドル
    PRECT  pRect    // RECT構造体へのポインタ
);

RECT構造体は座標を指定する構造体のことです。領域の左上の座標は常に(0,0)です。

WM_SIZE

WM_SIZEは、ウインドウのサイズが変わったとき(ウインドウ初回生成時も)に、実行されます。

MoveWindow()で、リッチエディットのウインドウサイズを変える。

ソースコードの入力

ソースコードは下記のように入れてください。

test.cpp

#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

#pragma comment(lib, "comctl32.lib")      // レバーコントロールの作成に必要

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    MSG msg;
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}

//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL , IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_HP);
    wcex.lpszClassName = TEXT("HP");
    wcex.hIconSm = LoadIcon(NULL , IDI_APPLICATION);

    return RegisterClassEx(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: メイン ウィンドウを作成します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hWnd = CreateWindow(TEXT("HP"), TEXT("HP"), WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:  メイン ウィンドウのメッセージを処理します。
//
//  WM_CREATE  - ウインドウ作成時の処理
//  WM_SIZE    - ウインドウサイズ変更時の処理
//  WM_COMMAND - アプリケーション メニューの処理
//  WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    static HWND hRebar;       // レバーコントロールのハンドル
    RECT Rect;                // RECT構造体
    INITCOMMONCONTROLSEX ic;  // INITCOMMONCONTROLSEX構造体
    REBARBANDINFO rbBand;     // REBARBANDINFO構造体

    switch (message)
    {
        case WM_CREATE:
       
            //コモンコントロールの初期化  
            ic.dwICC = ICC_COOL_CLASSES;
            ic.dwSize = sizeof(INITCOMMONCONTROLSEX); 
            InitCommonControlsEx(&ic); 

            //レバーコントロールの作成
            hRebar = CreateWindowEx(0,REBARCLASSNAME,TEXT(""),
                      WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |WS_CLIPCHILDREN | CCS_NODIVIDER,
                      0, 0, 0, 0,
                      hWnd, (HMENU)ID_COOL,
                      (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);

            // REBARBANDINFO構造体
            ZeroMemory(&rbBand, sizeof(REBARBANDINFO)); 
            // 構造体のサイズ
            rbBand.cbSize = sizeof(REBARBANDINFO);
            // マスクフラグ
            rbBand.fMask = RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE;
            // バンドのスタイルフラグ
            rbBand.fStyle = RBBS_CHILDEDGE;   
            // 最小値の高さ
            rbBand.cyMinChild = 30;
            // 最小値の幅
            rbBand.cxMinChild = 100;
            // レバーコントロールにのせる対象の子ウインドウのハンドル
            rbBand.hwndChild = CreateWindowEx(0, TEXT("BUTTON"), TEXT("Button1"),
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 0, 0, 0, 0,
                                 hRebar, (HMENU)ID_BUTTON1, 
                                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE) ,NULL);
            // レバーコントロールにボタン1を挿入 
            SendMessage(hRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);

            // レバーコントロールにボタン2を挿入
            rbBand.hwndChild = CreateWindowEx(0, TEXT("BUTTON"), TEXT("Button2"),
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 0, 0, 0, 100,
                                 hRebar, (HMENU)ID_BUTTON2, 
                                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL); 
            GetClientRect(hRebar, &Rect);
            rbBand.cx = Rect.right - 100;
            SendMessage(hRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
            break;

        case WM_SIZE:
            SendMessage(hRebar, WM_SIZE, wParam, lParam);
            break;

        case WM_COMMAND:
            wmId = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

上記の太線で示している箇所のみ追加です。

resource.h
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDC_HP 109
#define ID_COOL 150
#define ID_BUTTON1 151
#define ID_BUTTON2 152
#define IDC_BUTTON1 161
#define IDC_BUTTON2 162

上記の太線で示している箇所のみ追加です。

test.rc リソースファイル (変更なし)
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_HP MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "アプリケーションの終了(&X)", IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...", IDM_ABOUT
    END
END