FAQ10-1

Q. ウィンドウの縦横の比率を一定にするには

 はじめまして.「トラ技コンピュータ1995年7月号」で,WM_GETMINMAXINFOを使用して,縦方向もしくは横方向のみにサイズ変更なウィンドウの作成方法が載っていましたが,縦と横の比率を一定にするウィンドウ(例:横320のときは縦224,横640のときは縦448)はどのようにして作成するのでしょうか?
(角田 仁さんからの質問です)


A. メニューなどでサイズを選択するようにする

 WM_GETMINMAXINFOを使ってサイズ変更の可能な領域を変えることはできましたが,これと同じようにして,縦横比が一定なウィンドウを作ることは,残念ながらできません.しかたがないので,他の方法をとるしかありません.他の方法とは,
(1) 無理矢理にウィンドウサイズを調整する
(2) メニューなどでサイズを選択するようにする
(3) 自分でサイズ変更をエミュレートする

といったものが有力な手段ですが,(3)は難しいので(1)か(2)にしましょう.
 (1)の「無理矢理に」とは,ウィンドウサイズを変えた後に縦横比が一定になるように再度ウィンドウサイズを調整するという意味です.
 実際にWM_SIZEがウィンドウに送られてきたときに,MoveWindow()かSetWindowPos()を使って再度ウィンドウサイズを変えるようにすると,あたかも縦横比が一定のウィンドウのようになります.(図10-1).

[図10-1] :

 ただし,ここで問題があります.WM_SIZEの処理中にMoveWindow()やSetWindowPos()といったウィンドウサイズを変えるAPIを呼ぶと,MoveWindow()した後にまたWM_SIZEが発生してしまい,WM_SIZEの処理内でMoveWindow()して,またWM_SIZEが発生して...と,永久ループに陥ってしまう可能性があるということです.
 MoveWindow()で,変更前と変更後のサイズをぴったり同じサイズにサイズ変更すると,WM_SIZEは発生しないので,とりあえずこの方法でも問題はおきません.
 しかし,一応念のために,永久ループに陥らないように対処はしておくべきです.リスト10-1では,WM_SIZE処理中にWM_SIZEが発生する場合は,static変数にフラグを立てておき,フラグが立っている場合はWM_SIZEの処理はしないようにしています.これで安全になります.

[リスト10-1] :

#include <windows.h>

#define XRAITO 16
#define YRAITO 9

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) {
    switch( message ) {
        case WM_CREATE:
            break;
        case WM_DESTROY :
            PostQuitMessage(0);
            return 0;
        case WM_PAINT :{
            HDC         hdc;
            PAINTSTRUCT ps;
            RECT        rc;
            hdc = BeginPaint( hwnd ,&ps );
            GetClientRect (hwnd , &rc );
            char    sz[256];
            wsprintf( sz, "%d x %d", rc.right - rc.left, rc.bottom - rc.top );
            DrawText( hdc , sz ,-1 ,&rc,
                  DT_SINGLELINE | DT_CENTER | DT_VCENTER );
            EndPaint(hwnd ,&ps );
            return 0;
            }
        case WM_SIZE:{
            static BOOL fProcessing = FALSE;
            if( !fProcessing ) {
                static int  cxOld = 1;
                static int  cyOld = 1;
                RECT    rc;
                GetClientRect( hwnd, &rc );
                int cx = rc.right - rc.left;
                int cy = rc.bottom - rc.top;
                BOOL    f = FALSE;
                if( (cx * 100 / cxOld) > (cy * 100 / cyOld) ) {
                    f = TRUE;
                }
                if( cx == cxOld && cy != cyOld ) {
                    f = FALSE;
                } else if( cy == cyOld && cx != cxOld ) {
                    f = TRUE;
                }
                if( f ) {
                    cy = ( cx / XRAITO ) * YRAITO;
                } else {
                    cx = ( cy / YRAITO ) * XRAITO;
                }
                rc.right = rc.left + cx;
                rc.bottom = rc.top + cy;
                AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
                if( cx != cxOld || cy != cyOld ) {
                    fProcessing = TRUE;
                    SetWindowPos( hwnd, NULL, 0, 0,
                            rc.right - rc.left, rc.bottom - rc.top,
                            SWP_NOZORDER | SWP_NOMOVE );
                }
                cxOld = cx;
                cyOld = cy;
            } else {
                fProcessing = FALSE;
            }
            return 0;
            }
    }
    return  DefWindowProc(hwnd , message ,wParam ,lParam);
}

int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) {
    static char szAppName[] = "HelloWin" ;
    HWND        hwnd;
    MSG         msg;
    WNDCLASS    wc;

    wc.style        = CS_HREDRAW | CS_VREDRAW ;
    wc.lpfnWndProc  = (WNDPROC)WndProc ;
    wc.cbClsExtra   = 0;
    wc.cbWndExtra   = 0;
    wc.hInstance    = hInstance ;
    wc.hIcon        = LoadIcon ( NULL , IDI_HAND );
    wc.hCursor      = LoadCursor ( NULL , IDC_ARROW );
    wc.hbrBackground= GetStockObject ( WHITE_BRUSH );
    wc.lpszMenuName = NULL;
    wc.lpszClassName=szAppName;
    RegisterClass( &wc );
    
//  hwnd = CreateWindowEx ( WS_EX_CLIENTEDGE, szAppName,
    hwnd = CreateWindowEx ( NULL, szAppName,
                        "つきの",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        NULL, NULL,
                        hInstance, NULL);
    ShowWindow(hwnd , nCmdShow );
    UpdateWindow( hwnd );

    while( GetMessage(&msg , NULL ,0 ,0 ) ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (msg.wParam);
}
 (2)は,問題もなく簡単にできるはずなので,こちらのほうがいいかもしれません(図10-2).

[図10-2] :


Back to FAQ main page