Mesoscopic Programming

タコさんプログラミング専門

ウィンドウズプログラミング講座第5回:ツールバーとステータスバーと子ウィンドウ

概要

今回はツールバーとステータスバーと子ウィンドウを一気に追加します。
これでかなりアプリらしくなります。
まだまだ序盤なのでテヘペロっと行きます。

実行画面

ソースファイル
  1. Window.h
  2. Window.cpp
  3. main.h
  4. main.cpp
実行ファイル
  1. Windows.zip


ツールバー

初期化
class App : public Window
{
    enum
    {
        TB_FILE_NEW,
        TB_FILE_OPEN,
        TB_FILE_SAVE,
        TB_HELP,
        MAX_TB_BUTTON
    };

    HWND hToolBar;
};

BOOL App :: Init( int nCmdShow )
{
    DWORD dwStyle;
    BOOL  result;

    const TBBUTTON buttons[] =
    {
        { STD_FILENEW,  ID_FILE_NEW,  TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0 },
        { STD_FILEOPEN, ID_FILE_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0 },
        { STD_FILESAVE, ID_FILE_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0 },
        { 0,            0,            TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0 },
        { STD_HELP,     ID_APP_ABOUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0 },
    };

    dwStyle   = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS;
    hToolBar  = CreateToolbarEx( hWnd, dwStyle, ID_TOOLBAR, MAX_TB_BUTTON, HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
                                 buttons, sizeof buttons / sizeof ( TBBUTTON ), 0, 0, 15, 16, sizeof ( TBBUTTON ) );
    dwStyle   = GetWindowLong( hToolBar, GWL_STYLE ) | TBSTYLE_FLAT;
    result    = SetWindowLong( hToolBar, GWL_STYLE, dwStyle );
}
解説

気になる点としては、ウィンドウスタイルとして作成時に TBSTYLE_FLAT を設定すると何故かうまく行かないこと。
作成後に SetWindowLong() で設定するとうまく行くのに。まあ気にしないでおこう。

処理
LRESULT App :: OnWmSize()
{
    SendMessage( hToolBar, WM_SIZE, wparam, lparam );

    return 0;
}

LRESULT App :: OnTtnGetDispInfo()
{
    switch ( id )
    {
    case ID_FILE_NEW :  lpnmtdi->lpszText = _T( "新規作成" );   break;
    case ID_FILE_OPEN : lpnmtdi->lpszText = _T( "開く…" );     break;
    case ID_FILE_SAVE : lpnmtdi->lpszText = _T( "保存…" );     break;
    case ID_APP_ABOUT : lpnmtdi->lpszText = _T( "アバウト…" ); break;
    }

    return 0;
}
解説

親ウィンドウがサイズ変更されたとき、子ウィンドウであるツールバーもサイズ変更しなければならないのでサイズメッセーセージを送ろう。
あとツールチップを表示するのにウィンドウクラスも修正しました。
WM_NOTIFY の TTN_GETDISPINFO に対応しています。


ステータスバー

初期化
class App : public Window
{
    HWND hStatBar;
};

BOOL App :: Init( int nCmdShow )
{
    DWORD dwStyle;
    BOOL  result;

    dwStyle   = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_BOTTOM;
    hStatBar  = CreateStatusWindow( dwStyle, NULL, hWnd, ID_STATBAR );
    result    = SetWindowText( hStatBar, appName );
}
解説

ステータスバーはツールバーと違ってほとんど何もすることが無い。

処理
LRESULT App :: OnWmSize()
{
    SendMessage( hStatBar, WM_SIZE, wparam, lparam );
}
解説

ツールバーと同じく親のサイズ変更に対応するだけ。


子ウィンドウ

ツールバーとステータスバーを追加すると、メインウィンドウの使えるクライアントエリアが狭くなるので、そこを子ウィンドウにしてしまおう。
そうすれば子ウィンドウは自分のクライアントエリアを全部使えるので分かりやすい。

初期化
class Child : public Window
{
public :

    static const LPCTSTR className;

    BOOL    Init( Window * parent );
    LRESULT OnWmPaint();
};

const LPCTSTR Child :: className  = "ClassChild";

BOOL Child :: Init( Window * parent )
{
    WNDCLASSEX  wcx;
    DWORD       dwStyle;
    HWND        hWnd;

    wcx.cbSize          = sizeof ( WNDCLASSEX );
    wcx.style           = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc     = WindowProc;
    wcx.cbClsExtra      = 0;
    wcx.cbWndExtra      = sizeof ( HANDLE );
    wcx.hInstance       = GetModuleHandle( NULL );
    wcx.hIcon           = NULL;
    wcx.hCursor         = LoadCursor( NULL, IDC_ARROW );
    wcx.hbrBackground   = ( HBRUSH ) ( COLOR_WINDOW + 1 );
    wcx.lpszMenuName    = NULL;
    wcx.lpszClassName   = className;
    wcx.hIconSm         = NULL;

    RegisterClassEx( & wcx );

    dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
    hWnd    = CreateWindowEx( WS_EX_CLIENTEDGE, className, NULL, dwStyle,
                              0, 0, 0, 0,
                              parent->hwnd, NULL, GetModuleHandle( NULL ), this );

    return TRUE;
}
解説

初期化は親ウィンドウとほとんど一緒。

処理
LRESULT App :: OnWmSize()
{
    RECT rc;
    RECT rc2;
    RECT rc3;

    GetClientRect( hwnd, & rc );
    GetWindowRect( hToolBar, & rc2 );
    GetWindowRect( hStatBar, & rc3 );

    rc.top      = ( rc2.bottom - rc2.top );
    rc.bottom  -= ( rc3.bottom - rc3.top );

    MoveWindow( child.hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE );

    return 0;
}

LRESULT Child :: OnWmPaint()
{
    PAINTSTRUCT ps;
    HDC         hdc  = BeginPaint( hwnd, & ps );
    LPCTSTR     text = _T( "子ウィンドウ" );

    TextOut( hdc, 0, 0, text, ( int ) strlen( text ) );
    EndPaint( hwnd, & ps );

    return 0;
}
解説

子ウィンドウのサイズ変更は後々のことを考えて親ウィンドウ側で行います。
ツールバーとステータスバーのサイズ分を引いてフィットします。
あとなんとなくペイントメッセージにも対応しときました。

その他

ウィンドウクラスをかなり修正しましたけど詳しくはソースを見てね。

では、サイナラ。