10.2(화) 실습-1

2007/10/13 15:16

1. 메뉴 추가( 오너드로우 )
[ more.. | less.. ]

// 특정 메뉴를 OWNERDRAW로 변경하고 ItemData에 색상을 넣어 준다.
void AddMenuColor( HMENU hMenu, UINT id, COLORREF color )
{
    ModifyMenu( hMenu, id, MF_BYCOMMAND | MF_OWNERDRAW, id, "" );
    // ItemData에 색상을 기록해 둔다.
    MENUITEMINFO mi = { 0 };
    mi.cbSize = sizeof(mi);
    mi.dwItemData = (DWORD)color;
    mi.fMask = MIIM_DATA;
    SetMenuItemInfo( hMenu, id, FALSE, &mi );    // FALSE :2번째 인자가 ID라는 의미.TRUE:POSITION
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch( msg )
    {   
    case WM_CREATE:
        {
            // 그리기 메뉴 1단계 - 메뉴항목의 속성을 OWNERDRAW로 변경한다.
            HMENU h1 = GetMenu( hwnd );
            HMENU h2 = GetSubMenu( h1, 0 );

            AddMenuColor( h2, ID_RED, RGB( 255, 0, 0 ) );
            AddMenuColor( h2, ID_BLUE, RGB( 0, 0, 255 ) );
            AddMenuColor( h2, ID_GREEN, RGB( 0, 255, 0 ) );
           
            // ModifyMenu(
            //    h2,                // 팝업메뉴 핸들
            //    ID_RED,            // ID 또는 Position
            //    MF_BYCOMMAND | MF_OWNERDRAW,
            //    // 2번째 파라미터의 해석 방법 | 속성...
            //    // MF_BYCOMMAND(ID), MF_BYPOSITION(순서)
            //    ID_RED,            // 변경할 ID
            //    "빨간색"            // 변경할 Text
            //    );
        }
        return 0;
    case WM_MEASUREITEM:
        {
            // 그리기 메뉴 2단계
            // OS 가 구조체를 만들어서 포인터를 보내준다. 그 구조체에 크기를 채워준다.
            LPMEASUREITEMSTRUCT p = (LPMEASUREITEMSTRUCT)lParam;

            if( p->itemID == ID_RED || p->itemID == ID_BLUE || p->itemID == ID_GREEN )
            // 메뉴 Item ID 조사
            {
                p->itemWidth = GetSystemMetrics( SM_CYMENU ) * 4;
                p->itemHeight = GetSystemMetrics( SM_CYMENU );
            }
        }
        return 0;
    case WM_DRAWITEM:
        {
            // 그리기 메뉴 3단계 - Item 에 원하는 그림을 그린다.
            LPDRAWITEMSTRUCT p = (LPDRAWITEMSTRUCT)lParam;

            HDC hdc = p->hDC;
            RECT rc = p->rcItem;    // 그려야 하는 Item의 크기
            int state = p->itemState;    // Item의 상태
            int id = p->itemID;

            if( id == ID_RED || p->itemID == ID_BLUE || p->itemID == ID_GREEN )
            {
                RECT rcTemp = rc;

                // 선택여부를 조사한다.
                COLORREF c;
                /////////////////////////////////////////////////////
                if( state & ODS_SELECTED )    // 선택여부를 나타내는 필드
                {
                    c = GetSysColor( COLOR_HIGHLIGHT );
                    InflateRect( &rcTemp, -2, -2 );    // 사각형을 줄인다.
                }
                else
                {
                    c = GetSysColor( COLOR_MENU );
                    InflateRect( &rcTemp, -5, -5 );    // 사각형을 줄인다.
                }
                /////////////////////////////////////////////////////
                HBRUSH hBrush1 = CreateSolidBrush( c );
                FillRect( hdc, &rc, hBrush1 );

                // ItemData에서 색상을 얻는다.
                COLORREF color = (COLORREF)(p->itemData);
                HBRUSH hBrush = CreateSolidBrush( color );
                FillRect( hdc, &rcTemp, hBrush );

                DeleteObject( hBrush1 );
                DeleteObject( hBrush );
            }
        }
        return 0;

    case WM_RBUTTONUP:
        return DefWindowProc( hwnd, msg, wParam, lParam );    // MSDN에서 강조함!!확인!

    // ContextMenu 나타내기 - Tray에서 나타내려면 WM_RBUTTONUP을 사용해라.!
    case WM_CONTEXTMENU:
        {
            // 1. 메뉴 Load
            HMENU hMenu = LoadMenu( GetModuleHandle(0), MAKEINTRESOURCE(IDR_MENU1) );

            // 2. 나타낼 팝업 메뉴 핸들 얻기
            HMENU hSubMenu = GetSubMenu( hMenu, 0 );

            // 3. 현재 커서의 위치에 나타낸다.
            POINT pt;
            GetCursorPos( &pt );

            TrackPopupMenu(
                hSubMenu, TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
                pt.x, pt.y,        // 좌표
                0,                // Reserved(사용안함)
                hwnd,            // 메뉴 메세지를 받을 윈도우
                0 );            // 사용안함.

            // LoadIcon, LoadCursor는 Destroy 할 필요없다.
            // LoadMenu 는 Destroy 해야 한다. --- MSDN 참고.
            DestroyMenu( hMenu );
        }
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}

2. 모달 다이얼로그 값 읽어오기
[ more.. | less.. ]

// DialogBoxParam 을 사용하기 위해선 구조체가 필요하다.
// 각 컨트롤에서 값을 꺼내기 위해 사용한다.
typedef struct _DATA
{
    int cx;
    int cy;
}DLGDATA;

// DialogBox를 나타내려면 DialogProc가 있어야 한다.
BOOL CALLBACK DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    static DLGDATA* pData;

    switch( msg )
    {
    case WM_INITDIALOG:
        {
            // 부모를 사용가능하게 한다.
            pData = (DLGDATA*)lParam;

            // 구조체 값으로 컨트롤을 초기화 한다.
            SetDlgItemInt( hDlg, IDC_EDIT1, pData->cx, 0 );
            SetDlgItemInt( hDlg, IDC_EDIT2, pData->cy, 0 );

            // 부모를 사용가능하게 한다.
            //EnableWindow( GetParent( hDlg ), TRUE );
        }
        return TRUE;

    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case IDOK:
            {
                // 값을 꺼내서 부모가 전달해 준 구조체에 넣는다.
                BOOL bRet = 0;

                pData->cx = GetDlgItemInt( hDlg, IDC_EDIT1, &bRet, 0 );
                pData->cy = GetDlgItemInt( hDlg, IDC_EDIT2, &bRet, 0 );
                /*
                char buf[256];
                // 결국 각 Control의 ID를 핸들로 변경하면 다양한 윈도우 관련 함수를 사용가능하다.
                //HWND hEdit = GetDlgItem( hDlg, IDC_EDIT1 );
                //GetWindowLong( hEdit, buf, 256 );

                GetDlgItemText( hDlg, IDC_EDIT1, buf, 256 );
                MessageBox( 0, buf, "", MB_OK );
                */

                //////////////////////////////////////////////////////////////////////////
               
                EndDialog( hDlg, IDOK );    // 모달 Dialog를 닫는다.
            }
            break;
        case IDCANCEL:    EndDialog( hDlg, IDCANCEL ); break;
        }
        return TRUE;    // 메세지를 처리한 경우
    }
    return FALSE;    // 메세지를 처리 하지 않은 경우
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static int sx, sy;

    switch( msg )
    {
        RECT aa;

    case WM_LBUTTONDOWN:
        {
            DialogBox( 0, 0, 0, 0 );
            // 값을 꺼내오기 위해서 구조체 생성
            DLGDATA data;
            data.cx = sx;
            data.cy = sy;
            // 모달 Dialog 나타내기
            UINT ret = DialogBoxParam(
                GetModuleHandle( 0 ),            // hInstance
                MAKEINTRESOURCE( IDD_DIALOG1 ),    // 리소스 ID
                hwnd,                            // 부모 윈도우 핸들
                (DLGPROC)DlgProc,                // 메세지 함수
                (LPARAM)&data                    // WM_INITDIALOG의 lParam으로 간다.
                );

            if ( ret == IDOK )                    // EndDialog의 리턴값
            {
                // 구조체에서 값을 꺼내서 static 변수에 넣어 놓고 사용한다.
                sx = data.cx;
                sy = data.cy;

                InvalidateRect( hwnd, 0, TRUE );
            }
        }
        return 0;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint( hwnd, &ps );

            TextOut( hdc, sx, sy, "hello", 5 );
            EndPaint( hwnd, &ps );
        }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}

4. 메뉴적재
[ more.. | less.. ]
 // 메뉴를 적재하는 3가지 방법중 1번째
 // 1. 윈도우 클래스에 메뉴 리소스 ID를 등록한다.

 wc.lpszMenuName = IDR_MENU1;

 // 메뉴를 붙이는 2번쨰 방법.
 // 2. CreateWindowEx 9번째 인자를 사용한다.

 hMenu = LoadMenu( hInstance, MAKEINTRESOURCE(IDR_MENU1) );
 SetMenu( hwnd, hMenu );

// 3. 핸들을 얻어내서 추가하기
 HMENU h1 = GetMenu( hwnd );    // 메뉴 바의 핸들을 얻는다.
 HMENU h2 = GetSubMenu( h1, 0 );    // h1의 1번째 팝업 메뉴의 핸들을 얻는다.

 MENUINFO mi = { 0 };
 mi.cbSize = sizeof( mi );
 mi.hbrBack = CreateSolidBrush( RGB( 0, 255, 0 ) );
 mi.fMask = MIM_BACKGROUND;
  // 팝업 메뉴를 관리하는 구조체를 변경한다.
 SetMenuInfo( h2, &mi );

 //AppendMenu( h2, MF_STRING, 50001, "추가메뉴" );
 //// 메뉴 바에도 직접 추가 할 수 있다.
 //AppendMenu( h1, MF_STRING, 50002, "AAA" );
 /// 메뉴 바에 추가한 경우 즉시 그리게 한다.
 //DrawMenuBar( hwnd );


5.  전체 화면 흉내내기
[ more.. | less.. ]
           if ( bFull == FALSE )
            {
                bFull = TRUE;

                hMenu = GetMenu( hwnd );    // 메뉴 핸들을 보관하고
 
              SetMenu( hwnd, 0 ); // 메뉴를 떼어낸다.

                ModifyStyle( hwnd, WS_CAPTION, 0 );
                ShowWindow( hwnd, SW_MAXIMIZE );
            }
            else
            {
                bFull = FALSE;
                SetMenu( hwnd, hMenu );
                hMenu = 0;
                ModifyStyle( hwnd, 0, WS_CAPTION );
                ShowWindow( hwnd, SW_SHOWNORMAL );
            }

6. 모달리스 ( 메시지 루프에서 다이얼로그 메시지 받기 )
[ more.. | less.. ]

    while ( GetMessage( &msg, 0, 0, 0) )
    {   
        // 모달리스는 자체 루프를 가지고 있지 않다.
        // 그래서... IsDialogMessage()를 직접 호출해야 한다.

        if ( !IsWindow( g_hDlg ) || !IsDialogMessage( g_hDlg, &msg ))
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }
// DialogBox를 나타내려면 DialogProc가 있어야 한다.
BOOL CALLBACK DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
    static DLGDATA* pData;

    switch( msg )
    {
    case WM_INITDIALOG:
        {
            // 부모를 사용가능하게 한다.
            pData = (DLGDATA*)lParam;

            // 구조체 값으로 컨트롤을 초기화 한다.
            SetDlgItemInt( hDlg, IDC_EDIT1, pData->cx, 0 );
            SetDlgItemInt( hDlg, IDC_EDIT2, pData->cy, 0 );

            // 부모를 사용가능하게 한다.
            //EnableWindow( GetParent( hDlg ), TRUE );
        }
        return TRUE;

    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case IDOK:
            {
                // 값을 꺼내서 부모가 전달해 준 구조체에 넣는다.
                BOOL bRet = 0;

                pData->cx = GetDlgItemInt( hDlg, IDC_EDIT1, &bRet, 0 );
                pData->cy = GetDlgItemInt( hDlg, IDC_EDIT2, &bRet, 0 );

                // Data를 꺼내서 구조체에 넣어두고, 부모에게 메세지를 보내서 즉시 update 한다.
                SendMessage( GetParent(hDlg), WM_USER+100, 0, 0 );

            }
            break;
        case IDCANCEL:
            DestroyWindow( hDlg );
            g_hDlg = 0;    // 그래야 다음번 모달리스 Dialog가 나타난다.

            break;
        }
        return TRUE;    // 메세지를 처리한 경우
    }
    return FALSE;    // 메세지를 처리 하지 않은 경우
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static int sx, sy;

    // 값을 꺼내오기 위해서 구조체 생성
    static DLGDATA data;

    switch( msg )
    {
    case WM_USER+100:
        sx = data.cx;
        sy = data.cy;

        InvalidateRect( hwnd, 0, TRUE );
        return 0;

    case WM_LBUTTONDOWN:
        {
            if ( g_hDlg == 0 )
            {
                data.cx = sx;
                data.cy = sy;
                // 모달리스 Dialog 나타내기
                g_hDlg = CreateDialogParam(
                    GetModuleHandle( 0 ),            // hInstance
                    MAKEINTRESOURCE( IDD_DIALOG1 ),    // 리소스 ID
                    hwnd,                            // 부모 윈도우 핸들
                    (DLGPROC)DlgProc,                // 메세지 함수
                    (LPARAM)&data                    // WM_INITDIALOG의 lParam으로 간다.
                    );
                ShowWindow( g_hDlg, SW_SHOW );
            }
            else
            {
                // Focus를 이동해서 사용자에게 알려준다.
                SetFocus( g_hDlg );
            }
        }
        return 0;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint( hwnd, &ps );
            TextOut( hdc, sx, sy, "hello", 5 );
            EndPaint( hwnd, &ps );
        }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam);
}



Tags

API, 실습