Win32_API - 막대바 컨트롤(Scroll Bar)

컴퓨터/Win32-API

728x90
반응형

서론

다양한 응용프로그램에서 Scroll Bar의 사용빈도는 매우 높습니다. 

본문에서는 Scroll Bar를 Win32를 통해 만드는 방법과 정보에 대해서 알아보고, 간단한 실습 코드를 작성해 보겠습니다.

 

0. 사전 정보 

 

 

Scroll Bar - Win32 apps

이 섹션에는 스크롤 막대와 함께 사용되는 프로그래밍 요소에 대한 정보가 포함되어 있습니다.

docs.microsoft.com

Scroll Bar를 이용하는 프로그래밍 요소를 한번 살펴보고 오면 도움이 됩니다.

 

1.Scroll Bar 만들기 - CreateWindow

CreateWindowW(L"scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 50, 50, 100, 20, hWnd, NULL, hInst, NULL);

다른 컨트롤과 마찬가지로 scrollbar라는 이름으로 정의되어 있는 윈도우를 불러줍니다. 

 

  • SBS_HORZ - 가로 막대 스크롤 바를 생성하기 위한 스타일
  • 이 스크롤바의 크기는 생성되는 윈도우의 가로, 세로, 폭, 높이와 동일합니다.

2.Scroll Bar 처리를 위한 메세지 

지금 생성한 스크롤바는 모양은 표시되지만 아무런 동작을 하지 않습니다. 

하지만 계속해서 메세지를 발생하고 있습니다. 스크롤바를 동작시키면 가로 또는 세로의 형태에 따라 아래와 같은 메시지가 발생하게 됩니다.

  • WM_HSCROLL (가로 스크롤 바)
  • WM_VSCROLL (세로 스크롤 바)

지금 예제에서는 가로 스크롤바를 만들었으니, 가로 스크롤 바의 동작일때의 콜백 함수를 처리해 줍시다.

    case WM_HSCROLL:

이때 각각의 스크롤바의 내용이 움직였을 때 다양한 메시지가 발생합니다.

https://docs.microsoft.com/ko-kr/windows/win32/controls/about-scroll-bars

 

 

스크롤 막대 정보 - Win32 apps

창은 창의 클라이언트 영역보다 큰 데이터 개체(예: 문서 또는 비트맵)를 표시할 수 있습니다.

docs.microsoft.com

콜백 함수 안의 전체 소스코드는 아래와 같이 작성될 것입니다.

    case WM_HSCROLL:
        switch(LOWORD(wParam)) 
        {
        case SB_LINELEFT:
            
            break;
        case SB_PAGELEFT:

            break;
        case SB_LINERIGHT:
            
            break;

        case SB_PAGERIGHT:
            
            break;
        case SB_THUMBTRACK:
            
            break;
        }

3.Scrollbar 범위 설정하고 제어하기.

스크롤바 컨트롤을 생성하였다면 스크롤바가 어떤 녀석을 제어할지 지정해 주어야 합니다.

본문의 예제에서는 0~255까지의 숫자를 변경하기 위한 스크롤바를 만들어 보겠습니다.

스크롤바가 생성되는 시점에서 SetScrollRange함수를 이용하여 범위를 지정해 줍니다.

 

함수의 원형을 살펴보면 다음과 같습니다.

SetScrollRange(
    HWND hWnd,		//윈도우 핸들 값
    int nBar,		//스크롤 타입 (SB_CTL,SB_HORZ,SB_VERT) 
    int nMinPos, 	//최소 값
    int nMaxPos, 	//최대 값
    BOOL bRedraw); 	//TRUE = 스크롤 바 갱신 / FALSE 스크롤 바 비갱신

 

위의 내용을 토대로 다시 한번 WM_CREATE에 소스코드를 추가해 주겠습니다.

    case WM_CREATE:
        hScroll = CreateWindowW(L"scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 50, 50, 100, 20, hWnd, NULL, hInst, NULL);
        SetScrollRange(hScroll, SB_CTL, 0, 255, TRUE);
        break;

 

계속해서 스크롤 바의 포지션을 알아두어야 하기 때문에 전역 변수를 하나 선언하고 WM_HSCROLL에서 각각의 메세지마다 값을 처리하도록 합시다.

    case WM_HSCROLL:
        switch(LOWORD(wParam)) 
        {
        case SB_LINELEFT:			//좌 버튼 누를 때  
            pos = max(0, pos-1);
            break;
        case SB_PAGELEFT:			//좌 바 누를 때
            pos = max(0, pos - 5);
            break;
        case SB_LINERIGHT:			//우 버튼 누를 때
            pos = min(255, pos + 1);
            break;
        case SB_PAGERIGHT:			//우 바 누를 때
            pos = min(255, pos + 5);
            break;
        case SB_THUMBTRACK:			//스크롤 단추가 움직일 때
            pos = HIWORD(wParam);
            break;
        }

각각의 동작에 따라서 위치를 기억하기 위한 전역변수 pos의 값을 변환하게 만들어 주었습니다.

max와 min 메크로 함수를 이용하여 기존에 정해둔 범위인 0~255안에 들도록 해주었습니다.

또한 스크롤 단추를 통해 스크롤바를 제어하면, wParam에 HIWORD에 위치 값이 전송됩니다.

 

여기까지 작성해도 아직 스크롤바가 움직이지는 않습니다. 

SetScrollPos 함수를 이용해서 스크롤 버튼의 위치를 갱신해 주어야 합니다.

SetScrollPos 함수의 원형은 다음과 같습니다.

SetScrollPos(
    HWND hWnd,
    int nBar,
    int nPos,
    BOOL bRedraw);

 

이제 WM_HSCROLL에서 각 스크롤 명령에 따른 처리가 끝난 후, 위치를 갱신해 주도록 합시다.

    case WM_HSCROLL:
        switch(LOWORD(wParam)) 
        {
        case SB_LINELEFT:
            pos = max(0, pos-1);
            break;
        case SB_PAGELEFT:
            pos = max(0, pos - 5);
            break;
        case SB_LINERIGHT:
            pos = min(255, pos + 1);
            break;
        case SB_PAGERIGHT:
            pos = min(255, pos + 5);
            break;
        case SB_THUMBTRACK:
            pos = HIWORD(wParam);
            break;
        }

        SetScrollPos(hScroll, SB_CTL, pos, TRUE);

이제 스크롤 바가 움직이는 것을 확인할 수 있습니다.

4. 값을 관찰해 보도록 합시다.

이제 원하는 값을 스크롤바를 통해서 잘 제어하고 있는지 값을 출력하여 확인해 보도록 하겠습니다.

기존에 사용했던 TextOut함수를 이용하여 확인해 보도록 하겠습니다.

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            WCHAR form[100];
            wsprintfW(form,L"%d",pos);
            TextOutW(hdc, 10, 10, form,lstrlen(form));
            EndPaint(hWnd, &ps);
        }
        break;

WM_PAINT에서 처리를 해주었으니, 스크롤이 갱신되는 시점에서 WM_PAINT를 사용할 수 있도록 처리도 해주도록 합시다.

.....
        SetScrollPos(hScroll, SB_CTL, pos, TRUE);
        InvalidateRect(hWnd, NULL, TRUE);
        UpdateWindow(hWnd);

 

5. 전체 소스코드 보기

// Windows 헤더 파일
#include <windows.h>
// C 런타임 헤더 파일입니다.
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
HWND hScroll;
int pos;
// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);


int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{

    //윈도우 창 구조체 정의 및 적용
    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 = L"Test";
    wcex.hIconSm = NULL;
    RegisterClassExW(&wcex);

    //적용한 윈도우 생성 및 업데이트
    hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
    HWND hWnd = CreateWindowW(L"Test", L"Test", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    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;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        hScroll = CreateWindowW(L"scrollbar", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 50, 50, 100, 20, hWnd, NULL, hInst, NULL);
        SetScrollRange(hScroll, SB_CTL, 0, 255, TRUE);
        break;
    case WM_HSCROLL:
        switch(LOWORD(wParam)) 
        {
        case SB_LINELEFT:
            pos = max(0, pos-1);
            break;
        case SB_PAGELEFT:
            pos = max(0, pos - 5);
            break;
        case SB_LINERIGHT:
            pos = min(255, pos + 1);
            break;
        case SB_PAGERIGHT:
            pos = min(255, pos + 5);
            break;
        case SB_THUMBTRACK:
            pos = HIWORD(wParam);
            break;
        }

        SetScrollPos(hScroll, SB_CTL, pos, TRUE);
        InvalidateRect(hWnd, NULL, TRUE);
        UpdateWindow(hWnd);

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
            WCHAR form[100];
            wsprintfW(form,L"%d",pos);
            TextOutW(hdc, 10, 10, form,lstrlen(form));
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
728x90
반응형

Commnet

G91개발일지

Gon91(지구일)

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

TODAY :

YESTER DAY :

TOTAL :