MDI(Multiple Document Interface)とステータスバーをサポートしたサンプル

icon 項目のみ表示/展開表示の切り替え

概要

MDI(Multiple Document Interface)は下図の様な1つのアプリケーンの中にドキュメントを表示する複数のウィンドウが存在するインターフェースです。
本プログラムはMDIにステータスバーを追加したサンプルです。

本ページでは、MDI(Multiple Document Interface)サンプル1にステータスバーを付加するための変更点のみ記述しております。
ステータスバーについては、以下のページを参照してください。
ステータスバー

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsアプリケーション

実行環境

Windows 7 EnterPrise Service Pack 1 64bit
Windows 10 Home 32/64bit

プログラムソースの概要

mdi2.cpp

FrameWndProc

フレームウィンドウのプロシージャーです。
WM_CREATE
CreateStatusWindow APIによりステータスバーを作成します。
ステータスバーにSB_SETPARTSメッセージを送信しステータスバーの分割数と幅を設定します。
WM_SIZE
ステータスバーにWM_SIZEメッセージを送信しステータスバーの大きさと位置を調整します。
GetWindowRect APIによりステータスバーのウィンドウサイズを取得します。
WM_SIZEメッセージのLPARAMよりウィンドウサイズを取得しステータスバーの大きさ分を差し引いて MoveWindow APIによりクライアントウィンドウの大きさを変更します。
戻り値はメッセージは処理済みであることを示すため0を返す必要があります。

DocProc

ドキュメントウィンドウのプロシージャーです。
WM_MDIACTIVATE
ドキュメントウィンドウがアクティブになった時に呼び出されます。
LPARAMにはアクティブになるウィンドウのハンドル、WPARAMには非アクティブになるウィンドウのハンドルが格納されています。
LPARAMと自身のウィンドウハンドルを比較します。比較結果によりステータスバーの文字列を設定します。
同一
アクティブドキュメントウィンドウのテキスト
異なる場合
空白にする

プログラムソース

MDI(Multiple Document Interface)サンプル1からの変更箇所を着色しています。

mdi2.cpp

//       MDI(Multiple Document Interface)とステータスバーをサポートしたサンプル

#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#include "resource.h"
        
#pragma comment(lib,"comctl32.lib")

#define IDM_FIRSTCHILD 200      //ドキュメントウィンドウの最初のID番号 以降自動的に1ずつ加算される
#define ID_STATUS      100             // ステータスバーのID

#ifdef _WIN64
        typedef LONG_PTR WINDOW_LONG_PTR;
#else
        typedef LONG WINDOW_LONG_PTR;
#endif

LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DocProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK CloseAllProc(HWND, LPARAM);
int MyRegisterWC(WNDPROC, LPCTSTR, HBRUSH);

TCHAR szFrameClassName[] = _TEXT("mdi_org");    //フレームウィンドウクラス
TCHAR szChildDoc[] = _TEXT("document_org"); //ドキュメント

HMENU hMenuFirst, hMenuDoc;
HMENU hMenuFirstWnd, hMenuDocWnd;
HINSTANCE hInst;
HWND hStatus;

int doc_no=0;   //      ドキュメント(子ウィンドウ)番号

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   TCHAR* lpsCmdLine, int nCmdShow){
        MSG msg;
        HWND hFrame, hClient;

        ::hInst = hCurInst;
//      フレームウィンドウのクラスの登録   
        if(!MyRegisterWC(FrameWndProc, szFrameClassName,(HBRUSH)(COLOR_APPWORKSPACE + 1)))
                return FALSE;
//      ドキュメントウィンドウのクラスの登録   
        if(!MyRegisterWC(DocProc, szChildDoc, (HBRUSH)GetStockObject(WHITE_BRUSH)))
                return FALSE;
//      フレームウィンドウのメニューを読み込む
        ::hMenuFirst = LoadMenu(hInst, _TEXT("MYMENU"));
        ::hMenuFirstWnd = GetSubMenu(hMenuFirst, 0);
//      ドキュメントウィンドウのメニューを読み込む
        ::hMenuDoc = LoadMenu(hInst, _TEXT("MYDOCUMENT"));
        ::hMenuDocWnd = GetSubMenu(hMenuDoc, 1);
//      フレームウィンドウの作成
        hFrame = CreateWindow(
                szFrameClassName,
                _TEXT("Multiple Document Interface2"),        //      ウィンドウタイトル
                WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                ::hMenuFirst,
                ::hInst,
                NULL);
        hClient = GetWindow(hFrame, GW_CHILD);
        ShowWindow(hFrame, nCmdShow);
        UpdateWindow(hFrame);

        while (GetMessage(&msg, NULL, 0, 0)) {
                if (!TranslateMDISysAccel(hClient, &msg)) {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }
        }
        DestroyMenu(hMenuDoc);
        return int(msg.wParam);
}

//      フレームウィンドウのプロシージャー

LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
        static HWND hClient;

       int sb_size[] = { 100, 200, -1 };
        HWND hChild;
        switch (msg) {
                case WM_CREATE:{
                       hStatus = CreateStatusWindow(WS_CHILD | SBARS_SIZEGRIP | CCS_BOTTOM | WS_VISIBLE, _TEXT(""), hWnd, ID_STATUS);
                        SendMessage(hStatus, SB_SETPARTS, (WPARAM)3, (LPARAM)(LPINT)sb_size);
                        // クライアントウィンドウの作成
                        CLIENTCREATESTRUCT ccs;
                        ccs.hWindowMenu = hMenuFirstWnd;
                        ccs.idFirstChild = IDM_FIRSTCHILD;//ドキュメントウィンドウの最初のID番号
                        hClient = CreateWindow(
                        _TEXT("MDICLIENT"),
                        NULL,
                        WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
                        0, 0, 0, 0,
                        hWnd,
                        (HMENU)1,
                        ::hInst,
                        (LPSTR)&ccs);
                        return 0;
                }
                case WM_COMMAND:
                        switch (LOWORD(wp)) {
                                case IDM_NEW:{  //      新規作成メニュー選択時 子ウィンドウの作成
                                        MDICREATESTRUCT mdic;
                                        TCHAR str[64];

                                        wsprintf(str, _TEXT("テキスト %d"), ::doc_no);
                                        TCHAR* text=_tcsdup(str);

                                        wsprintf(str, _TEXT("DOCUMENT[%d]"), ::doc_no);
                                        mdic.szClass = szChildDoc;
                                        mdic.szTitle = str;
                                        mdic.hOwner = ::hInst;
                                        mdic.x = CW_USEDEFAULT;
                                        mdic.y = CW_USEDEFAULT;
                                        mdic.cx = CW_USEDEFAULT;
                                        mdic.cy = CW_USEDEFAULT;
                                        mdic.style = 0;
                                        mdic.lParam = (LPARAM)text; //ドキュメントウィンドウ作成時に渡すパラメータ
                                        hChild = (HWND)SendMessage(hClient, WM_MDICREATE, 0,
                                        (LPARAM)&mdic);
                                        ++ ::doc_no;
                                        return 0;
                                }
                                case IDM_CLOSE: //      アクティブなドキュメントウィンドウを閉じる
                                        hChild = (HWND)SendMessage(hClient, WM_MDIGETACTIVE, 0, 0);
                                        if (hChild)
                                                SendMessage(hClient, WM_MDIDESTROY, (WPARAM)hChild, 0);
                                        return 0;
                                case IDM_EXIT:  //      プログラムを終了
                                        SendMessage(hWnd, WM_CLOSE, 0, 0);
                                        return 0;
                                case IDM_CLOSEALL:      //      全てのドキュメントウィンドウを閉じる
                                        EnumChildWindows(hClient, &CloseAllProc, 0);
                                        return 0;
                                case IDM_TILE:  //      ドキュメントウィンドウを並べて表示
                                        SendMessage(hClient, WM_MDITILE, 0, 0);
                                        return 0;
                                case IDM_CASCADE:       //      ドキュメントウィンドウを重ねて表示
                                        SendMessage(hClient, WM_MDICASCADE, 0, 0);
                                        return 0;
                                case IDM_ARRANGE:       //      アイコンの整列(最小化されたドキュメントウィンドウのアイコンを整列)
                                        SendMessage(hClient, WM_MDIICONARRANGE, 0, 0);
                                        return 0;
                                default:
                                        hChild = (HWND)SendMessage(hClient, WM_MDIGETACTIVE, 0, 0);
                                        if (IsWindow(hChild))
                                                SendMessage(hChild, WM_COMMAND, wp, lp);
                                        break;
                        }
                        break;
               case WM_SIZE:{
                        int wx,wy,sx,sy;
                        RECT rc;
                        SendMessage(hStatus, WM_SIZE, wp, lp);
                        GetWindowRect(hStatus, &rc);
                        sx=rc.right-rc.left;
                        sy=rc.bottom-rc.top;
                        wx = LOWORD(lp);
                        wy = HIWORD(lp);
                        wy-=sy;
                        MoveWindow(hClient,0,0,wx,wy,TRUE);
                        return 0;
                }
                case WM_CLOSE:  //      フレームウィンドウの終了時に呼び出される。
                        SendMessage(hWnd, WM_COMMAND, IDM_CLOSEALL, 0);
                        if (GetWindow(hClient, GW_CHILD))
                                return 0;
                        break;
                case WM_DESTROY:
                        PostQuitMessage(0);
                        return 0;
        }
        return DefFrameProc(hWnd, hClient, msg, wp, lp);
}

// ドキュメントウィンドウのプロシージャー

LRESULT CALLBACK DocProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
        static HWND hClient, hFrame;
        TCHAR* str;

        switch (msg) {
                case WM_CREATE:{
                        hClient = GetParent(hWnd);
                        hFrame = GetParent(hClient);
                        LPCREATESTRUCT lpcs=(LPCREATESTRUCT)lp;
                        LPMDICREATESTRUCT lpmdis=(LPMDICREATESTRUCT)lpcs->lpCreateParams;

                        str=(TCHAR*)lpmdis->lParam;  //      子ウィンドウ作成時に指定されたパラメータを取得
                        // 各ウィンドウごとに個別に使用できるメモリにフレームウィンドウから渡された文字列を保存
                        SetWindowLongPtr(hWnd, GWLP_USERDATA, (WINDOW_LONG_PTR)str); 
                        return 0;
                }
                case WM_PAINT:{
                        PAINTSTRUCT ps;
                        HDC hdc=BeginPaint(hWnd,&ps); 
                        // 各ウィンドウごとに個別に使用できるメモリから文字列を取得
                        str=(TCHAR*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
                        TextOut(hdc,0,0,str,int(_tcslen(str)));
                        EndPaint(hWnd,&ps); 
                        break;
                }
                case WM_SIZE:
                        break;
                case WM_MDIACTIVATE:
                        if (lp == (LPARAM)hWnd){
                                SendMessage(hClient, WM_MDISETMENU, (WPARAM)::hMenuDoc, (LPARAM)::hMenuDocWnd);
                               str=(TCHAR*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
                                SendMessage(hStatus, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)str);
                        }else{
                                SendMessage(hClient, WM_MDISETMENU, (WPARAM)::hMenuFirst, (LPARAM)::hMenuFirstWnd);
                               SendMessage(hStatus, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)_TEXT(""));
                        }
                        DrawMenuBar(hFrame);
                        return 0;
        }
        return (DefMDIChildProc(hWnd, msg, wp, lp));
}

//      指定するMDIドキュメントウィンドウを閉じる(EnumChildWindowsから呼び出される)

BOOL CALLBACK CloseAllProc(HWND hWnd, LPARAM lp){
        SendMessage(GetParent(hWnd), WM_MDIDESTROY, (WPARAM)hWnd, 0);
        return TRUE;
}

//      ウィンドウクラスを登録

int MyRegisterWC(WNDPROC lpfnWndProc, LPCTSTR lpszClassName, HBRUSH hbrBack){
        WNDCLASSEX wc;

        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = lpfnWndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInst;        //インスタンス
        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = hbrBack;
        wc.lpszMenuName = NULL;    //メニュー名
        wc.lpszClassName = lpszClassName;
        wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        return(RegisterClassEx(&wc));
}

resource.h

#define IDM_NEW  1000
#define IDM_EXIT        1001
#define IDM_CLOSE       1003
#define IDM_CASCADE     1005
#define IDM_TILE        1006
#define IDM_CLOSEALL    1007
#define IDM_ARRANGE     1008

mdi2.rc

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

MYMENU MENU DISCARDABLE 
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "新規作成(&N)",                IDM_NEW
        MENUITEM "終了(&X)",                    IDM_EXIT
    END
END

MYDOCUMENT MENU DISCARDABLE 
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "新規作成(&N)",                IDM_NEW
        MENUITEM "閉じる(&C)",                  IDM_CLOSE
        MENUITEM SEPARATOR
        MENUITEM "終了(&X)",                    IDM_EXIT
    END
    POPUP "ウィンドウ(&W)"
    BEGIN
        MENUITEM "重ねて表示(&C)",              IDM_CASCADE
        MENUITEM "並べて表示(&T)",              IDM_TILE
                MENUITEM "すべて閉じる(&L)",            IDM_CLOSEALL
        MENUITEM "アイコンの整列(&A)",          IDM_ARRANGE
    END
END

ソースファイルと実行ファイルのダウンロード

ダウンロード mdi2.zip(26.0kByte)
ZIPファイルに含まれるファイル
mdi2.cpp
resource.h
mdi2.rc
mdi2.exe