WindowsAPI - 실습 - 그림판 구현하기 4 (버튼 제어 추가 & 오류 수정)

컴퓨터/Win32-API

728x90
반응형

서론

팬과 지우기 버튼에 토글 기능을 구현하고, 색상판에 스크롤바를 제어할 수 있는 기능을 추가하도록 하겠습니다.

그리고 몇가지 오류 수정을 하도록 합시다.

 

 

WindowsAPI - 실습 - 그림판 구현하기 3 ( UI부분 구현 )

서론 본문에서는 지난 포스트에서 작성한 구조에 컨트롤 버튼 등을 만드는 UI작업을 진행해 보도록 하겠습니다. 2개의 토글 버튼과 색상을 제어할수 있는 색상판을 만들어 보도록 합시다. 이전

blog-of-gon.tistory.com

 

 

FindWindow 함수의 변경 - FindWindowW -> FindWindowExW

해당 함수를 사용하는 과정에서 정상적으로 윈도 핸들 값을 못 찾아왔습니다. 

원인은 자식윈도우를 찾는 과정 때문에 알맞은 함수가 아녔습니다. 

따라서 해당 함수들을 수정해 주었습니다.

하단의 전체 소스코드를 참고해 주세요!

그리기 Or 지우기 토글 버튼 동작 구현

그리기와 지우기 기능을 토글로 선택하고, 값을 출력할수 있도록 2가지 함수를 만들었습니다.

//토글 값 셋팅
void SetFunction(WPARAM wParam, LPARAM lParam,HWND hWnd)
{
    if (HIWORD(wParam) == BN_CLICKED)
    {
        switch (LOWORD(wParam))
        {
        case 1:
        {
            if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
                SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_SETCHECK, BST_UNCHECKED, 0);
                return ;
            }
            else {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);

            }
        }
        case 2:
        {
            if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
                SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_SETCHECK, BST_UNCHECKED, 0);
                return ;
            }
            else {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);
            }
        }

        } // 스위치문 종료

    }
    return ;
}

//토글 값 받아오기
int GetFunction(WPARAM wParam, LPARAM lParam, HWND hWnd)
{
    // 1 == 그리기 2 == 지우기 없을 시 0
    if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 1;
    if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 1;
    else return 0;

}

이후 WM_COMMAND에서 처리해 주도록 합시다. 아직 GetFunction함수는 사용하지 않습니다.

    case WM_COMMAND:
    {
        //토글 버튼 처리하기
        SetFunction(wParam, lParam, hWnd);


        //각종 버튼에 따른 동작 
        break;
    }

스크롤 동작 구현

스크롤 또한 버튼을 누름에 따라 동작을 해주어야 합니다. 

//스크롤 동작
void SetScrollFunction(WPARAM wParam, LPARAM lParam)
{

    switch (LOWORD(wParam))
    {
    case SB_LINELEFT:
        SetScrollPos((HWND)lParam, SB_CTL, max(0,GetScrollPos((HWND)lParam,SB_CTL)-1), TRUE);
        break;
    case SB_PAGELEFT:
        SetScrollPos((HWND)lParam, SB_CTL, max(0, GetScrollPos((HWND)lParam, SB_CTL) - 5), TRUE);
        break;
    case SB_LINERIGHT:
        SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 1), TRUE);
        break;
    case SB_PAGERIGHT:
        SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 5), TRUE);
        break;
    case SB_THUMBTRACK:
        SetScrollPos((HWND)lParam, SB_CTL, HIWORD(wParam), TRUE);
        break;
    }
}

이후 WM_HSCROLL에서 해당 함수를 호출하고 다시 선택된 색상판을 그려주어야 하므로 WM_PAINT를 호출해 줍니다.

    case WM_HSCROLL:
    {
        SetScrollFunction(wParam, lParam);
        InvalidateRect(hWnd, NULL, TRUE);
        UpdateWindow(hWnd);
        break;
    }

 

현재 선택된 색상 갱신하기

스크롤 바를 이용하여 선택되는 색상을 제어하기 위해서 함수를 만들도록 합시다. 

스크롤 바로부터 값을 받아와 현재 적용되는 색상판의 갱신을 해주도록 합시다.

//색상 선택
void SetColor(HWND hWnd,HDC hdc)
{
    int status = 0;
    if (hdc == NULL) {
        hdc = GetDC(hWnd);
        status = 1;
    }


    //스크롤 Bar의 값 받아오기
    int R = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL);
    int G = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL);
    int B = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL);

    HBRUSH newBrush = CreateSolidBrush(RGB(R, G, B));
    HBRUSH OldBrush = (HBRUSH)SelectObject(hdc, newBrush);
    //선택된 색상 보여주기
    Rectangle(hdc, 80, 30, 150, 120);

    //스크롤 Bar의 값 표시해주기
    WCHAR text[10];
    wsprintf(text, L"R : %d", R );
    TextOutW(hdc, 360, 40, text, lstrlenW(text));
    wsprintf(text, L"G : %d", G );
    TextOutW(hdc, 360, 70, text, lstrlenW(text));
    wsprintf(text, L"B : %d", B );
    TextOutW(hdc, 360, 100, text, lstrlenW(text));
    SelectObject(hdc, OldBrush);
    DeleteObject(newBrush);
    
    if (status == 1)
    {
        ReleaseDC(hWnd, hdc);
    }
}

이후 WM_PAINT 호출이 발생하면 해당 색상을 그려주도록 합니다.

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        SetColor(hWnd,hdc);
        EndPaint(hWnd, &ps);
        break;
    }

전체 소스코드 및 다음 Step

이제 기능버튼과,색상을 선택할 수 있게 됐습니다.

각각의 기능을 구현하면서, 더블 버퍼링을 구현하여 원활한 기능이 동작하도록 만들어 봅시다.

또한 이미지나 화면 레이아웃을 조금 더 효과적으로 변경해 보도록 합시다.

  • Paint.cpp
더보기
// Paint.cpp : Defines the entry point for the application.
//

#include "framework.h"
#include "Paint.h"

//함수 원형 선언 부분
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

// 전역변수
HINSTANCE hInst;                               

// WinMain함수 부분
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

    /*윈도우 설정 및 메인 윈도우를 생성합니다.*/
    HWND hWnd = InitMainWindowSet(hInstance, &WndProc, L"Paint");
    //정상적으로 생성되었다면 윈도우를 갱신합니다.
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    MSG msg;
    // 기본 메시지 루프입니다:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

// WndProc 함수부분
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        //윈도우 생성 메세지
    case WM_CREATE:
    {
        //버튼 생성
        CreateButton(L"Pen", 20, 20, 50, 50, (HMENU)1, hWnd, hInst);
        CreateButton(L"Erase", 20, 75, 50, 50, (HMENU)2, hWnd, hInst);
        //버튼 생성 및 이미지 씌우기

        //색상 테이블 생성
        CreateRGBTable(L"RGBtable", 80, 30, 150, 120, (HMENU)10, hWnd, hInst);
        break;
    }

    //메인 기능 그리기 Or 지우기
    case WM_COMMAND:
    {
        //토글 버튼 처리하기
        SetFunction(wParam, lParam, hWnd);


        //각종 버튼에 따른 동작 
        break;
    }
    //스크롤 기능
    case WM_HSCROLL:
    {
        SetScrollFunction(wParam, lParam);
        InvalidateRect(hWnd, NULL, TRUE);
        UpdateWindow(hWnd);
        break;
    }


        /*각종 마우스 동작에 따른 컨트롤*/
    case WM_MOUSEMOVE:
    {
        break;
    }
    case WM_LBUTTONDOWN:
    {

        break;
    }
    case WM_LBUTTONUP:
    {
        break;
    }
    case WM_RBUTTONDOWN:
    {
        break;
    }
    case WM_RBUTTONUP:
    {
        break;
    }
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        SetColor(hWnd,hdc);
        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
  • MySource.cpp
더보기
#include "MySource.h"

//윈도우 창 설정
HWND InitMainWindowSet(HINSTANCE hInstance, WNDPROC WndProc,const WCHAR* name)
{
    //윈도우 창 구조체 설정 및 적용하기
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = NULL;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = name;
    wcex.hIconSm = NULL;
    RegisterClassExW(&wcex);

    //해당 윈도우 창을 가지고와서 윈도우 창 생성하기
    return CreateWindowW(name, name, WS_MAXIMIZE| WS_SYSMENU,
        100, 100, 750, 750, nullptr, nullptr, hInstance, nullptr);
}

//버튼생성하기 
void CreateButton(const WCHAR* name,LONG x,LONG y,LONG width ,LONG height, HMENU id, HWND hWnd, HINSTANCE hInst)
{
    CreateWindowW(L"button", name, WS_CHILD | WS_VISIBLE | BS_CHECKBOX, x,y,width,height, hWnd, id, hInst,NULL);
}
//색상 선택 도구 만들기
void CreateRGBTable(const WCHAR* name, LONG left, LONG top, LONG right, LONG bottom, HMENU id, HWND hWnd, HINSTANCE hInst)
{
    
    //RGB의 스크롤 바 생성 및 범위 설정
    CreateWindowW(L"scrollbar", L"R", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+15, right + 30, 15, hWnd, id, hInst, NULL);
    SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL, 0, 255, TRUE);
    CreateWindowW(L"scrollbar", L"G", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+40, right + 30, 15, hWnd, id+1, hInst, NULL);
    SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL, 0, 255, TRUE);
    CreateWindowW(L"scrollbar", L"B", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+65, right + 30, 15, hWnd, id+2, hInst, NULL);
    SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL, 0, 255, TRUE);
    //SetColor(hWnd);
}
//색상 선택
void SetColor(HWND hWnd,HDC hdc)
{
    int status = 0;
    if (hdc == NULL) {
        hdc = GetDC(hWnd);
        status = 1;
    }


    //스크롤 Bar의 값 받아오기
    int R = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL);
    int G = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL);
    int B = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL);

    HBRUSH newBrush = CreateSolidBrush(RGB(R, G, B));
    HBRUSH OldBrush = (HBRUSH)SelectObject(hdc, newBrush);
    //선택된 색상 보여주기
    Rectangle(hdc, 80, 30, 150, 120);

    //스크롤 Bar의 값 표시해주기
    WCHAR text[10];
    wsprintf(text, L"R : %d", R );
    TextOutW(hdc, 360, 40, text, lstrlenW(text));
    wsprintf(text, L"G : %d", G );
    TextOutW(hdc, 360, 70, text, lstrlenW(text));
    wsprintf(text, L"B : %d", B );
    TextOutW(hdc, 360, 100, text, lstrlenW(text));
    SelectObject(hdc, OldBrush);
    DeleteObject(newBrush);
    
    if (status == 1)
    {
        ReleaseDC(hWnd, hdc);
    }
}


//이미지 씌우기
//더블버퍼링
//색상선택
//버튼 제어

//토글 값 셋팅
void SetFunction(WPARAM wParam, LPARAM lParam,HWND hWnd)
{
    if (HIWORD(wParam) == BN_CLICKED)
    {
        switch (LOWORD(wParam))
        {
        case 1:
        {
            if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
                SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_SETCHECK, BST_UNCHECKED, 0);
                return ;
            }
            else {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);

            }
        }
        case 2:
        {
            if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
                SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_SETCHECK, BST_UNCHECKED, 0);
                return ;
            }
            else {
                SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);
            }
        }

        } // 스위치문 종료

    }
    return ;
}

//토글 값 받아오기
int GetFunction(WPARAM wParam, LPARAM lParam, HWND hWnd)
{
    // 1 == 그리기 2 == 지우기 없을 시 0
    if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 1;
    if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 1;
    else return 0;

}

//스크롤 동작
void SetScrollFunction(WPARAM wParam, LPARAM lParam)
{

    switch (LOWORD(wParam))
    {
    case SB_LINELEFT:
        SetScrollPos((HWND)lParam, SB_CTL, max(0,GetScrollPos((HWND)lParam,SB_CTL)-1), TRUE);
        break;
    case SB_PAGELEFT:
        SetScrollPos((HWND)lParam, SB_CTL, max(0, GetScrollPos((HWND)lParam, SB_CTL) - 5), TRUE);
        break;
    case SB_LINERIGHT:
        SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 1), TRUE);
        break;
    case SB_PAGERIGHT:
        SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 5), TRUE);
        break;
    case SB_THUMBTRACK:
        SetScrollPos((HWND)lParam, SB_CTL, HIWORD(wParam), TRUE);
        break;
    }
}
//등등 기능을 쪼개서 하나씩 추가할 예정
728x90
반응형

Commnet

G91개발일지

Gon91(지구일)

91년생 공학엔지니어의 개발일지

TODAY :

YESTER DAY :

TOTAL :