SDK Index Previous page Next page

CoolBarを作る


はじめに

今回も前回に続いて、IEに依存しそうなCoolBarの作り方を説明します。CoolBarというのは、IE4.0をインストールした後のエクスプローラーのツールバーのような、位置を自由に動かすことの出来るコントロールです。正確にはその上に作られたコントロールや子ウインドウの位置を自由に動かせるように出来るコントロールです。だから、ツールバーだけでなくいろいろなものをCoolBarに入れることが出来ます。
サンプルプログラムではコンボボックスとツールバーを持ったCoolBarを作っています。

coolbar

プログラム

プログラムの説明をしますが、長くなりそうなのでWinMainとInitWindow(メインウインドウの作成)の部分は省略します。

1 ウインドウプロシージャ

はじめはウインドウプロシージャです。
//ウインドウプロシージャ
int CALLBACK WndProc(HWND hWnd, unsigned wMessage,
                  WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;

    switch (wMessage)
    {
        case WM_CREATE:
            CenterWindow(hWnd);
            CreateRebar(hWnd);
            return  0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return  0;

        case WM_SIZE:
            MoveRebar(hWnd);
            break;

        case WM_PAINT:
            hdc = BeginPaint (hWnd, &ps);
            EndPaint (hWnd, &ps);
            return  0;

        case WM_NOTIFY:
            return DoNotify(hWnd, wParam, lParam);

    }
    return (DefWindowProc(hWnd, wMessage, wParam, lParam));
}
ここで、重要なのはWM_CREATE,WM_SIZE,WM_NOTIFYの3つの部分です。
といっても、見ればわかると思いますが。WM_CREATEでRebarを作り、WM_SIZEでは、Rebarの大きさをウインドウサイズに合わせます。そして、WM_NOTIFYでは、Rebarコントロールからのメッセージを処理します。
では、Rebarとは何でしょうか?実はこれがCoolbarです。なぜかCoolbarコントロールではなくRebarコントロールという名前になっています。たぶん、Coolbarというのは後で付けた名前なんでしょう。

2 Coolbarを作って、コンボボックスとツールバーを入れる

Coolbarを作っているところです。今回のメインですね。
この関数は、かなり長いですが大きく3つの部分からなります。
1 Coolbarコントロールを作る
2 コンボボックスを作ってCoolbarに入れる
3 ツールバーを作ってCoolbarに入れる

HWND WINAPI CreateRebar(HWND hWndOwner)
{
    REBARINFO   rbi;
    REBARBANDINFO   rbBand;
    RECT        rc;
    HWND        hWndCB, hWndTB, hWndRB;
    DWORD       dwBtnSize;
    INITCOMMONCONTROLSEX icex;

//Coolbarを作る
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC  = ICC_COOL_CLASSES|ICC_BAR_CLASSES;
    InitCommonControlsEx(&icex);

    hWndRB = CreateWindowEx(WS_EX_TOOLWINDOW,
                                REBARCLASSNAME,
                                NULL,
                                WS_BORDER |
                                WS_CHILD |
                                WS_VISIBLE |
                                WS_CLIPCHILDREN |
                                WS_CLIPSIBLINGS |
                                RBS_VARHEIGHT |
                                RBS_BANDBORDERS | 
                                CCS_NODIVIDER | 
                                CCS_NOPARENTALIGN |
//                              CCS_VERT |  //垂直バー
                                0,
                                0,0,200,100,
                                hWndOwner,
                                (HMENU)ID_REBAR,
                                Instance,
                                NULL);
    if(!hWndRB)   return NULL;

    rbi.cbSize = sizeof(REBARINFO);
    rbi.fMask  = 0;
    rbi.himl   = (HIMAGELIST)NULL;
    if(!SendMessage(hWndRB, RB_SETBARINFO, 0, (LPARAM)&rbi)) return NULL;

//コンボボックスを作ってCoolbarに入れる
    hWndCB=CreateComboBox(hWndRB);
    GetWindowRect(hWndCB, &rc);

    rbBand.cbSize = sizeof(REBARBANDINFO);
    rbBand.fMask  = RBBIM_TEXT |
                    RBBIM_BACKGROUND |
                    RBBIM_STYLE |
                    RBBIM_CHILDSIZE | 
                    RBBIM_SIZE |
                    RBBIM_CHILD | 
                    RBBIM_ID;
    rbBand.fStyle = RBBS_CHILDEDGE |
                    RBBS_FIXEDBMP |
                    RBBS_GRIPPERALWAYS;
    rbBand.hbmBack= LoadBitmap(Instance, MAKEINTRESOURCE(IDB_BACKGRND));  

    rbBand.wID        = ID_COMBOBOX;
    rbBand.lpText     = TEXT("ComboBox");
    rbBand.cch        = 2;
    rbBand.hwndChild  = hWndCB;
    rbBand.cxMinChild = rc.right - rc.left;
    rbBand.cyMinChild = rc.bottom - rc.top;
    rbBand.cx         = 200;

    SendMessage(hWndRB, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);

//ツールバーを作ってCoolbarに入れる
    hWndTB = CreateToolbar(hWndOwner);
    GetWindowRect(hWndTB, &rc);
    dwBtnSize = SendMessage(hWndTB, TB_GETBUTTONSIZE, 0,0);

    rbBand.fMask  = RBBIM_BACKGROUND |
                    RBBIM_STYLE |
                    RBBIM_CHILDSIZE | 
                    RBBIM_SIZE |
                    RBBIM_CHILD | 
                    RBBIM_ID |
                    0;

    rbBand.wID        = ID_TOOLBAR;
    rbBand.hwndChild  = hWndTB;
    rbBand.cxMinChild = rc.right - rc.left;
    rbBand.cyMinChild = HIWORD(dwBtnSize);
    rbBand.cx         = 150;

    SendMessage(hWndRB, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);

    return (hWndRB);
}

Coolbar

Coolbarを作る部分ではInitCommonControlsEx関数を使ってコモンコントロールを初期化した後、CreateWindowExを使ってCoolbarを作っています。Coolbarのクラス名はREBARCLASSNAMEです。これはcommctrl.hの中で定義されています。言い忘れていましたが、commctrl.hをインクルードしてcomctl32.libをリンクファイルに加えるのを忘れないようにしてください。
後、コントロールのスタイルですが、実は私もよくわかっていません。スタイルを消したりつけたりして自分で見つけてください。その次に、SendMessageでRB_SETBARINFOを送っていますが、これはイメージリストというものを使うときに必要になってきます。イメージリストを使わないときには、これと同じようにすれば大丈夫です。

コンボボックス

次に、コンボボックスを作ります。それが、CreateComboBoxという関数です。これは、ただのコンボボックスを作ってるだけなので、後でソースを載せるだけで説明はしません。コンボボックスを作ったら今度はそれをCoolbarに入れなければいけません。そのためには、REBARBANDINFO構造体に必要な情報を設定してRB_INSERTBANDメッセージを送ります。
この構造体のメンバはいっぱいあるのですが、ここでは今回使ったものだけを説明します。

rbBand.cbSize
 構造体の大きさを入れます。sizeofを使うだけ。

rbBand.fMask
 この構造体のどのメンバを使用するかを指定します。

RBBIM_TEXT          lpTextメンバを使用する
RBBIM_BACKGROUND    hbmBackメンバを使用する
RBBIM_STYLE         fStyleメンバを使用する
RBBIM_CHILDSIZE     cxMinChild, cyMinChild, cyChild, cyMaxChild,cyIntegral メンバを使用する
RBBIM_SIZE          cxメンバを使用する
RBBIM_CHILD         hwndChildメンバを使用する
RBBIM_ID            wIDメンバを有効にする
rbBand.fStyle
バーのスタイルを設定します。
RBBS_CHILDEDGE      バーの上下に境界線をつける
RBBS_FIXEDBMP       背景のビットマップを固定する
RBBS_GRIPPERALWAYS  サイズ変更用のグリップをつける
rbBand.hbmBack
背景用ビットマップのハンドルを指定する。これを使うと、コントロールの背景を変えれる。ビットマップはリソースに組み込んでおく。

rbBand.wID
コントロールのIDを設定する。

rbBand.lpText
これを使うと、コントロールの左にこの文字列が表示される。

rbBand.cch
lpTextのサイズらしいのだが、サンプルには2になっていた。よくわからん。

rbBand.hwndChild
Coolbarに入れるコントロール(子ウインドウ)のハンドル

rbBand.cxMinChild
横幅の最小値

rbBand.cyMinChild
高さの最小値

rbBand.cx
横幅の初期値

構造体の設定が終わったらSendMessageを使ってRB_INSERTBANDを送ります。この時、wParamにはコントロールを入れる場所(0を指定すると先頭に、−1を指定すると最後に追加される)、lParamにはさっきの構造体のアドレスを渡します。

ツールバー

今度は、ツールバーを作ってCoolbarに入れます。CreateToolbar関数でツールバーを作っています。ここで作るツールバーは、フラット+透明 な、ちょっと新しいタイプのツールバーです。このツールバーの作り方は後で説明します。
ツールバーを作ったらさっきと同じようにCoolbarに入れます。コンボボックスとの違いは、RBBIM_TEXTが無いのと、cyMinChildメンバがHIWORD(dwBtnSize)になっていることの2カ所です。 RBBIM_TEXTが無いのは、ツールバーの横に文字列を表示しないからで、cyMinChildが違うのはウインドウのサイズを使うと大きさが大きくなりすぎるからです。

3 コンボボックスを作る

コンボボックスを作ります。なんの変哲もないコンボボックスなので説明は省略します。
//コンボボックスの作成
HWND CreateComboBox(HWND hWnd)
{
    HWND    hWndCB;
    char    szString[64];
    int i;

    hWndCB = CreateWindowEx(   0, 
                        TEXT("combobox"), 
                        NULL,
                        WS_VISIBLE |
                        WS_CHILD | 
                        WS_TABSTOP |
                        WS_VSCROLL |
                        WS_CLIPCHILDREN | 
                        WS_CLIPSIBLINGS | 
                        CBS_AUTOHSCROLL | 
                        CBS_DROPDOWN | 
                        0,
                        0, 
                        0, 
                        100, 
                        200, 
                        hWnd, 
                        (HMENU)ID_COMBOBOX, 
                        Instance, 
                        NULL);

    SendMessage(hWndCB, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0));
   
    //コンボボックスにアイテムを入れる
    for(i = 0; i < 25; i++){
        wsprintf(szString, TEXT("Item %d"), i + 1);
        SendMessage(hWndCB, CB_ADDSTRING, 0, (LPARAM)szString);
    }
    return hWndCB;
}

3 ツールバーを作る

ツールバーを作ります。基本的に普通のツールバーと同じです。ちょっと新しいところは強調表示にしています。
//ツールバーの作成
TBBUTTON tbb[] = {
    {0, 0, TBSTATE_ENABLED, TBSTYLE_BUTTON , 0, 0},
    {1, 1, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0},
    {2, 2, TBSTATE_ENABLED, TBSTYLE_DROPDOWN, 0, 0},
    {3, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}
};

HWND CreateToolbar(HWND hWnd)
{
    HWND hTool;


    hTool = CreateToolbarEx(hWnd,
                            WS_CHILD |
                            CCS_NOPARENTALIGN |
                            CCS_NORESIZE |
                            CCS_NODIVIDER |
                            TBSTYLE_FLAT |
                            TBSTYLE_WRAPABLE,
                            ID_TOOLBAR,
                            4,
                            Instance,
                            IDB_TOOLBAR,
                            tbb,
                            4,
                            0, 0, 16, 15,
                            sizeof(TBBUTTON));

    SendMessage(hTool,TB_SETEXTENDEDSTYLE,0,TBSTYLE_EX_DRAWDDARROWS);
    return hTool;
}
3カ所ほどあります。まずは真ん中のツールバースタイルの所です。ここはほとんど強調表示になっています。それぞれの意味は、
CCS_NOPARENTALIGN
ツールバーが、ウインドウの上部に勝手に移動しないようにします。これをつけないと、ツールバーを動かすことができません。

CCS_NORESIZE
ツールバーの大きさを自由に変更できるようにします。これをつけないと、うまく大きさを変えれません。

CCS_NODIVIDER
ツールバーの上につく境界線を無くします。

TBSTYLE_FLAT
フラットなツールバーにします。これをつけると自動的に、透明なツールバーになります。

TBSTYLE_WRAPABLE
横幅が小さくなりすぎたときに自動的に折り返すようにします。Coolbarでは基本的に折り返しはないので、このフラグは必要ありませんがCoolbarを縦型にするときには必要です。

次は、残りの2カ所です。この二つはどちらもドロップダウンボタン付きのボタンを作るためのものです。サンプルの”C”のボタンをみてください。ボタンの右側に小さな三角がありますね、これがドロップダウンボタンです。ドロップダウンボタンを作るには、TBBUTTON構造体のボタンのスタイルをTBSTYLE_DROPDOWNにして、ツールバーの拡張スタイルとしてTBSTYLE_EX_DRAWDDARROWSをつけます。拡張スタイルの設定にはTB_SETEXTENDEDSTYLEメッセージを使います。
ドロップダウンボタンが押されると親ウインドウにWM_NOTIFYメッセージが送られてきます。その時のNOTIFYコードはTBN_DROPDOWNです。普通は、その時にメニューを表示します。

4 NOTIFYメッセージ

WM_NOTIFYメッセージの処理です。ドロップダウンボタンが押されたときにメニューを表示するために使っています。
//WM_NOTIFYメッセージの処理
BOOL DoNotify(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    #define lpNM   ((LPNMHDR)lParam)
    #define lpnmTB ((LPNMTOOLBAR)lParam)

    RECT      rc;
    TPMPARAMS tpm;
    HMENU     hMenu,hPopupMenu = NULL;    99/01/18訂正
    BOOL      bRet = FALSE;

    switch(lpNM->code){
        case TBN_DROPDOWN:
            SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT,
                    (WPARAM)lpnmTB->iItem, (LPARAM)&rc);

            MapWindowPoints(lpnmTB->hdr.hwndFrom,
                         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;

            hMenu=LoadMenu(Instance,MAKEINTRESOURCE(IDR_POPUP));    99/01/18訂正
            hPopupMenu = GetSubMenu(hMenu,0);

            TrackPopupMenuEx(hPopupMenu,
                          TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
                          rc.left, rc.bottom, hWnd, &tpm);
            DestroyMenu(hMenu);    99/01/18追加
            return (FALSE);
    }

    return FALSE;
}
特に大したことはしていないです。コードがTBN_DROPDOWNだったら、座標を調べてデスクトップの座標に変換して、TrackPopuMenuExでポップアップメニューを表示しているだけです。メニューはリソースエディタで作っておいてください。

5 Coolbarの大きさを調整する

メインウインドウの大きさが変更されたときに、Coolbarの大きさをそれに合わせて変えます。たぶん説明の必要はないと思います。みたままです。
//Coolバーをウインドウのサイズに合わせる
void MoveRebar(HWND hWnd)
{
    RECT  rc;
    int   x,y,cx,cy;

    GetClientRect(hWnd, &rc);

    x = 0;
    y = 0;
    cx = rc.right - rc.left;
    cy = rc.bottom - rc.top;
    MoveWindow( GetDlgItem(hWnd, ID_REBAR),x, y, cx, cy, TRUE);
}

おわりに

今回は、簡単なCoolbarの作り方を説明しました。これをつけるだけで、以外とかっこよくなるので便利なコントロールだと思います。またサンプルでは、メインウインドウの背景色を指定していますが、自分のプログラムにつけるときには背景色を0にしたほうがCoolbarがちらつかなくて良いです。


to sdk prev next