관리 메뉴

ㄴrㅎnㅂrㄹrㄱi

[강좌 9] 메뉴2 본문

API 관련/API 강좌모음

[강좌 9] 메뉴2

님투 2007. 10. 26. 03:40
반응형

[API]강좌(9)<--메뉴2




오늘은 메뉴 부분 마지막 강좌인 툴바와 툴팁 대해서 알바 보도록 하겠습니다.
먼저 용어를 정의 해보도록 하죠.
툴바가 무엇인지 모르지는 않겠죠? 한글로 도구모음이라고 번역이 되는데 이것은
메뉴 아래에 그림버튼 형식으로 자주 쓰이는 메뉴 아이템을 꺼내 놓은 것입니다.
MFC를 이용해서 프로그램을 만들면 이 툴바를 간단하게 구현할수 있지만 API는
조금 복잡한 구조를 띕니다. 그렇다고 아주 복잡한 형식은 아닙니다. 제가 쉽게
설명을 드릴테니 잘 보시기 바랍니다. 툴팁은 뭘까요? 툴바에 마우스의 커서를
대고 몇초간 기다리면 이 버튼이 어떤 기능을 하는지 풍성 도움말이 나올겁니다.
이것을 툴팁이라고 합니다.
자 그러면 툴바와 툴팁을 어떻게 구현하는지 알아 봅시다. 이 툴바와 툴팁을 구현
하면 메뉴 부분에 대한 것은 끝난것 같군요. 다음에는 상태바에 대해 알아보고
그래픽 부분으로 넘어가겠습니다.
툴바에 사용되는 그림은 윈도우즈에서 사용하는 비트맵 파일 포맷을 사용합니다.
비트맵 그림을 사용하니까 리소스 파일에서 이 그림을 정의 해주어야 겠죠?
그런데 예전에 알아본 비트맵 그림파일을 정의 해 준것과는 좀 다릅니다.
500 BITMAP "icon.bmp"
위와 같은 형태로 정의 해줍니다. 뭐가 다른지 알겠습니까? 우리가 예전에 비트맵
파일을 정의할때에는 멘 앞에 유저가 임의로 문자열을 정의해서 정의 했는데 숫
자로 정의하고 있잖아요? 그렇죠? 이것은 툴바를 생성하는 함수에서 비트맵 파라
미터 핸들을 숫자로 요구하기 때문입니다. 그렇다면 툴바로 사용할 그림은 어떤식
으로 그려야 할까요? 그것은 보통 16X16으로 그림을 그립니다. 단 버튼이 여러개
일때에 따로따로 그리는 것인 아니라 하나의 그림 파일로 연결해서 그려야 한다는
것입니다. 예를들어서 버튼이 4개인 툴바라고 가정합시다. 그러면 그 그림 사이즈
는 어떻게 될까요? 64X16이 되겠죠? 가로로 길쭉한 모양의 그림이 되겠네요.
툴바를 만들어줄 함수를 사용하기 위해서는 아래의 헤더 파일을 포함시켜야 합니
다.
#include <commctrl.h>
이것은 우리가 CreateWindow() 함수와 같은 어떤 윈도우를 생성할때 사용하는 함수
가 지원 하지 못하는 것까지 지원하도록 확장된 형태를 띄고 있는것입니다.
void InitCommonControls(void);
위에서 선언한 헤더 파일에서 지원하는 컨트롤을 사용하기 위해서는 윈도우를 생성
하는 CreatWindow() 함수 다음에 위 InitCommonControls()함수를 사용해야 합니다.
툴바로 사용할 그림은 하나죠? 그러나 그그림은 16크기로 구분되어져 있을 겁니다.
각 그림의 기능과 속성을 정의해줄 필요가 있습니다. 이때 TBBUTTON이라는
구조체를 이용해서 그 속성을 지정해줄수 있습니다.
typedef struct _TBBUTTON {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
#ifdef _WIN32
BYTE bReserved[2];
#endif
DWORD dwData;
int iString;
} TBBUTTON, NEAR* PTBBUTTON, FAR* LPTBBUTTON;
구조체의 원형을 잘 보여주고 있군요. 각 구조체 멤버의 의미를 알아 봅시다. 우선
첫번째 iBitmap이라는 것은 버튼의 인덱스를 의미하는 것입니다. 예를들어서 46X16
그림이라면 버튼이 4개이니 위 구조체를 배열로 해서 4개를 생성하고 첫번째 버튼
의 iBitmap은 0을 두번째는 1을 세번째, 네번째에는 각각 2, 3을 넣어주면 되는
것입니다. 두번째 맴버인 idCommand는 이 버튼이 생성되었을때 발생될 메시지를 의
미하는 것입니다. 다시말해서 아이디를 의미하는거죠. 우리가 앞에서 알아본 메뉴
도 아이템을 선택했을때 아이디가 메시지로 발생됐죠? 그거랑 같은 겁니다. 대부
분의 툴바는 메뉴 아이템중에 있는것을 만드는 경우가 많으므로 메뉴 아이템중 이
기능을 하는 아이템의 아이디를 지정하면 됩니다. 세번째 멤버인 fsState는 말그
대로 버튼의 상태를 지정해 주는 것입니다. 버튼의 상태는 아래의 지정된 예약어로
서 그 상태를 명시해 줄수 있습니다.
TBSTATE_ENABLED 일반적인 버튼을 생성합니다.
TBSTATE_PRESSED 눌린 형태의 버튼을 생성합니다.
TBSTATE_INDETERMINATE 그레이된 버튼을 생성합니다.
네번째 멤버인 fsStyle은 버튼의 스타일을 지정해 줄수 있는데 이때 지정될수 있는
예약어는 다음과 같습니다.
TBSTYLE_BUTTON 일반적인 버튼을 생성합니다.
TBSTYLE_CHECK 토글 버튼을 생성합니다. 한번 선택되면 들어가고 다시
선택하면 원래대로 나오는 형태의 버튼이죠.
TBSTYLE_SEP 버튼과 버튼을 약간 띄어 주는 역할로 메뉴 만드는 속성
중 SEPARATOR 기능과 같습니다.
나머지 멤버는 잘 쓰이지 않아 보통 0으로 지정해주면 됩니다.
자 그러면 정리해 봅시다. 비록 그림은 하나지만 그 그림속에 4개의 버튼이 있다면
위 구조체를 배열로서 4개를 선언하고 각각 속성을지정해주면 되는 것입니다. 저
장된 속성은 툴바를 만들어주는 함수의 파라미터로 사용되어 원하는 툴바를 생성해
주거든요.
WINCOMMCTRLAPI HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws,
UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT wBMID,
LPCTBBUTTON lpButtons, int iNumButtons, int dxButton, int dyButton,
int dxBitmap, int dyBitmap, UINT uStructSize);
툴바를 만들기 위해서 위 CreateToolbarEx() 함수를 사용합니다. 우와~~~ 복잡하
네요... 그쵸? 그런데 하나씩 알아보면 별것도 아닙니다.
hwnd 메인 윈도우의 핸들을 의미합니다.
ws 윈도우의 스타일을 지정하면 됩니다. 보통
WS_VISIBLE|WS_CHILD|WS_BORDER|TBSTYLE_TOOLTIPS 로 지정하
는데 처음 보는것이 있군요. 바로 마지막 속성인데 이것은 이
툴바에 툴팁을 사용하겠다는 의미입니다.
wID 전체 툴바에 대한 아이디를 지정하면 되는데 보통 -1을 지정합니
다. 왜냐하면 이 아이디는 잘 쓰이지 않거든요.
nBitmaps 그림버튼의 개수를 지정하면 됩니다. 위에서 알아 본대로면 4를
지정하면 되겠네요.
hBMInst 인스턴스 핸들을 지정하면 됩니다.
wBMID 툴바로 사용할 그림의 아이디인데 이것은 리소스 파일에서 정의한
숫자를 지정하면 됩니다. 위와 같은 겨우라면 500을 지정하면 되
겠죠?
lpButtons 바로 위에서 배운 툴바 구조체의 주소를 지정해 주면 됩니다.
iNumButtons 역시 툴바 버튼의 개수를 지정하면 됩니다. 4라고 지정하면 되겠
죠?
dxButton 보통 0이라고 지정하면 됩니다.
dyButton 보통 0이라고 지정하면 됩니다.
dxBitmap 버튼 그림 하나의 가로 크기를 지정하면 됩니다. 16이라고 하면
되죠.
dyBitmap 버튼 그림 하나의 세로 크기를 지정하면 됩니다. 16이라고 하면
되죠.
uStructSize 툴바 구조체의 크기를 지정하면 됩니다.
툴바 구조체를 선언해서 값을 채워주고 위 함수를 이용하면 간단하게 툴바가 구현
됩니다. 그렇게 복잡하지는 않죠? 그러면 이번에는 툴팁 처리하는 부분을 봅시다.
커서가 툴바에 놓여진채 몇초가 지나면 WM_NOTIFY라는 메시지가 발생됩니다. 그런
데 문제는 이 메시지가 꼭 커서가 툴바에 놓여있을때만 발생하는 것이 아니라 다른
일이 있을때도 발생될수 있다는 것입니다. 그러면 이것이 툴바에 커서가 있어서
발생한 메시지라는 것을 어떻게 알수 있을까요? 그것은 LPARAM 값을 보고 알수 있
습니다. LPARAM값을 특수한 구조체인 LPTOOLTIPTEXT로 캐스트 연산을 해서 그 안에
포함된 값이 정의된 값이면 이것이 툴바에 커서가 놓여저서 발생됐다고 판단이 되
는것입니다. 어렵습니까? 간단하게 예를 보죠?
LPTOOLTIPTEXT ttText;
case WM_NOTIFY :
ttText = (LPTOOLTIPTEXT)lParam;
if(ttText->hdr.code == TTN_NEEDTEXT)
{
.
.
.
}
자 여기까지입니다. 위의 것을 해석하려면 먼저 TOOLTIPTEXT라는 구조체의 원형을
보는 것이 이해하기가 쉬울겁니다.
typedef struct tagTOOLTIPTEXT {
NMHDR hdr;
LPSTR lpszText;
char szText[80];
HINSTANCE hinst;
UINT uFlags;
} TOOLTIPTEXT, FAR *LPTOOLTIPTEXT;
멤버중 모르는 변수 타입이 있죠? 바로 NMHDR이라는 타입인데 이것도 구조체입니
다. 다음은 이 구조체의 원형입니다.
typedef struct tagNMHDR
{
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
이젠 ttText->hdr.code 구문이 이해 가죠? 이 값이 TTN_NEEDTEXT면 툴바에 커서
가 놓여 있어서 툴팁에 들어갈 문자열이 필요하다는 것을 의미합니다. 구체적으
로 어떤 버튼에 커서가 놓여있는지는 ttText->hdr.idFrom에 그 아이디가 들어가
있습니다. 그러면 유저는 TOOLTIPTEXT의 두번째 멤벙인 lpszText에 문자열의 지
정해주면 그 문자열이 풍성 도움말로 나오게 되죠. 풍선 도움말이 나오는 구문을
예로 들어 볼까요?
switch(ttText->hdr.idFrom)
{
case 100 :
ttText->lpszText = "Open";
break;
}
100이라는 아이디를 가진 버튼에 커서가 놓여 있으면 Open이라는 풍선 도움말을
보내라는 구문입니다. 어렵습니까? 그렇다면 전체 예제 소스를 보시기 바랍니다.
아하~~~ 하고 이해가 갈겁니다. 아래 예제는 네개의 버튼을 가진 툴바와 툴팁을
구현한 예제입니다.
먼저 리소스 파일의 소스입니다.
#include <windows.h>
500 BITMAP "icon.bmp"
MyMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", 100
MENUITEM "&Close", 200
MENUITEM SEPARATOR
MENUITEM "&Save", 300
MENUITEM "Save &As", 400
END
POPUP "&Toolbar"
BEGIN
MENUITEM "&Hide", 600
MENUITEM "&Show", 700
END
END
프로그램 메인 소스입니다.
#include <windows.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateTOOLBAR(HWND, HINSTANCE);
HWND hToolbar;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create toolbar";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = "MyMenu";
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
InitCommonControls();
CreateTOOLBAR(hWnd, hInstance);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
LPTOOLTIPTEXT ttText;
switch(mesg)
{
case WM_COMMAND :
hDC = GetDC(hWnd);
switch(LOWORD(wParam))
{
case 100 :
TextOut(hDC, 100, 100, "Open Button Clicked", 20);
break;
case 200 :
TextOut(hDC, 100, 100, "Close Button Clicked", 21);
break;
case 300 :
TextOut(hDC, 100, 100, "Save Button Clicked", 20);
break;
case 400 :
TextOut(hDC, 100, 100, "Save As Button Clicked", 23);
break;
case 600 :
ShowWindow(hToolbar, SW_HIDE);
break;
case 700 :
ShowWindow(hToolbar, SW_RESTORE);
break;
}
ReleaseDC(hWnd, hDC);
return FALSE;
case WM_NOTIFY :
ttText = (LPTOOLTIPTEXT)lParam;
if(ttText->hdr.code == TTN_NEEDTEXT)
{
switch(ttText->hdr.idFrom)
{
case 100 :
ttText->lpszText = "Open";
break;
case 200 :
ttText->lpszText = "Close";
break;
case 300 :
ttText->lpszText = "Save";
break;
case 400 :
ttText->lpszText = "Save As";
break;
}
}
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
void CreateTOOLBAR(HWND hWnd, HINSTANCE hInst)
{
TBBUTTON tbToolbar[5];
tbToolbar[0].iBitmap = 0;
tbToolbar[0].idCommand = 100;
tbToolbar[0].fsState = TBSTATE_ENABLED;
tbToolbar[0].fsStyle = TBSTYLE_BUTTON;
tbToolbar[0].dwData = 0L;
tbToolbar[0].iString = 0;
tbToolbar[1].iBitmap = 1;
tbToolbar[1].idCommand = 200;
tbToolbar[1].fsState = TBSTATE_ENABLED;
tbToolbar[1].fsStyle = TBSTYLE_BUTTON;
tbToolbar[1].dwData = 0L;
tbToolbar[1].iString = 0;
tbToolbar[2].iBitmap = 0;
tbToolbar[2].idCommand = 0;
tbToolbar[2].fsState = TBSTATE_ENABLED;
tbToolbar[2].fsStyle = TBSTYLE_SEP;
tbToolbar[2].dwData = 0L;
tbToolbar[2].iString = 0;
tbToolbar[3].iBitmap = 2;
tbToolbar[3].idCommand = 300;
tbToolbar[3].fsState = TBSTATE_ENABLED;
tbToolbar[3].fsStyle = TBSTYLE_BUTTON;
tbToolbar[3].dwData = 0L;
tbToolbar[3].iString = 0;
tbToolbar[4].iBitmap = 3;
tbToolbar[4].idCommand = 400;
tbToolbar[4].fsState = TBSTATE_ENABLED;
tbToolbar[4].fsStyle = TBSTYLE_BUTTON;
tbToolbar[4].dwData = 0L;
tbToolbar[4].iString = 0;
hToolbar = CreateToolbarEx(hWnd,
WS_VISIBLE|WS_CHILD|WS_BORDER|TBSTYLE_TOOLTIPS,
-1,
5,
hInst,
500,
tbToolbar,
5,
0, 0, 16, 16,
sizeof(TBBUTTON)
);
}
1부 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2부를 시작 하겠습니다.
먼저 툴바를 사용하기 위해 아래의 헤더 파일을 포함 시켰습니다. 이미 설명 드린
부분이죠?
#include <commctrl.h>
윈도우 생성하는 CreateWindow() 함수 다음에 아래 함수도 사용해야 한다고 했을
겁니다.
InitCommonControls();
못보던 함수가 하나 있죠? 바로 아래 함수인데 이것은 필자가 만든 함수입니다.
이 함수 내부를 보도록 하죠.
CreateTOOLBAR(hWnd, hInstance);
void CreateTOOLBAR(HWND hWnd, HINSTANCE hInst)
{
TBBUTTON tbToolbar[5];
어허~~ 이렇게 이상한일이 발생할수가????? 버튼이 4개로 구성되어 있다고 했는데
왜 5개로 선언했을까요? 그것은 우리가 앞에서 알아본 버튼 스타일중 TBSTYLE_SEP
을 사용하기 위해서입니다. 이것은 버튼과 버튼을 구분짓는 역할만 하지만 그래도
하나의 경계 버튼이라고 볼수 있는것입니다. 실제로 사용될 버튼은 네개지만요.
tbToolbar[0].iBitmap = 0;
tbToolbar[0].idCommand = 100;
tbToolbar[0].fsState = TBSTATE_ENABLED;
tbToolbar[0].fsStyle = TBSTYLE_BUTTON;
tbToolbar[0].dwData = 0L;
tbToolbar[0].iString = 0;
리소스 파일에서 만든 메뉴 아이템중 100 값을 갖는게 있죠? 그것에 대한 버튼입
니다. 보면 알겠지만 아이디가 100으로 되어 있잖아요.
tbToolbar[1].iBitmap = 1;
tbToolbar[1].idCommand = 200;
tbToolbar[1].fsState = TBSTATE_ENABLED;
tbToolbar[1].fsStyle = TBSTYLE_BUTTON;
tbToolbar[1].dwData = 0L;
tbToolbar[1].iString = 0;
여기까지는 크게 어려운 부분이 없죠? 아래를 봅시다.
tbToolbar[2].iBitmap = 0;
tbToolbar[2].idCommand = 0;
tbToolbar[2].fsState = TBSTATE_ENABLED;
tbToolbar[2].fsStyle = TBSTYLE_SEP;
tbToolbar[2].dwData = 0L;
tbToolbar[2].iString = 0;
버튼의 인덱스가 2가 되어야 하는데 0이 되고 아이디도 0이죠? 이 버튼은 버튼의
역할을 갖는것이 아니라 버튼과 버튼을 띄어주는 역할을 하기 때문에 0으로 지정
한 것입니다.
tbToolbar[3].iBitmap = 2;
tbToolbar[3].idCommand = 300;
tbToolbar[3].fsState = TBSTATE_ENABLED;
tbToolbar[3].fsStyle = TBSTYLE_BUTTON;
tbToolbar[3].dwData = 0L;
tbToolbar[3].iString = 0;
tbToolbar[4].iBitmap = 3;
tbToolbar[4].idCommand = 400;
tbToolbar[4].fsState = TBSTATE_ENABLED;
tbToolbar[4].fsStyle = TBSTYLE_BUTTON;
tbToolbar[4].dwData = 0L;
tbToolbar[4].iString = 0;
나머지는 같군요.
hToolbar = CreateToolbarEx(hWnd,
WS_VISIBLE|WS_CHILD|WS_BORDER|TBSTYLE_TOOLTIPS,
-1,
5,
hInst,
500,
tbToolbar,
5,
0, 0, 16, 16,
sizeof(TBBUTTON)
);
실제로 등록한 값을 가지고 버튼을 생성하는 과정을 보여주고 있습니다. 버튼 개
수를 지정하는곳을 보면 버튼과 버튼을 띄어주는 것까지 합쳐서 5로 지정했음을
알수 있을겁니다.
}
이 과정으로 인해서 툴바를 만드는 과정은 끝난것입니다. 그러면 이번에는 툴팁
처리 하는 부분을 보죠.
case WM_NOTIFY :
ttText = (LPTOOLTIPTEXT)lParam;
if(ttText->hdr.code == TTN_NEEDTEXT)
{
switch(ttText->hdr.idFrom)
{
case 100 :
ttText->lpszText = "Open";
break;
case 200 :
ttText->lpszText = "Close";
break;
case 300 :
ttText->lpszText = "Save";
break;
case 400 :
ttText->lpszText = "Save As";
break;
}
}
return FALSE;
아이고 쉬워라~~~~~~~~~~~~~ 이미 다 설명이 된 거군요. 그쵸? 할게 없네요. 아
차~ 한가지 빠진 부분이 있군요.
바로 툴바를 보이게 하고 보이지 않게 하고 하는부분인데 보통 윈도우즈용 프로
그램을 보면 도구모음을 보이게 할수도 있고 보이지 않게도 할수 있는 메뉴 아이템
이 있을 겁니다. 그것을 구현한 부분을 봅시다.
ShowWindow(hToolbar, SW_HIDE);
위 함수는 많이 본거죠? 첫번째 파라미터는 툴바의 핸들을 의미하고 두번째 파라
미터는 툴바를 감추겠다는 의미입니다.
ShowWindow(hToolbar, SW_RESTORE);
반대 역할을 하는거군요. 다시 보이게 하는 역할을 합니다.
2부 끝~~~~~~~~~~~~~~~~~~~~
 
상태바를 만드는 예제인데 툴바를 구현할때와 크게 다른점은 없을 겁니다. 역시 이 상태바를 구현할때도 아래의 헤더 파일과 함수를 사용해야 합니다.
#include <commctrl.h>
InitCommonControls();
툴바를 구현할때도 사용했죠?
상태바를 보면 여러개로 나뉘어 있죠? 예를 들어 3개로 나뉘어 있다고 합시다.
프로그램에서 어떻게 나눌수 있을까요? 이 나눌 좌표를 지정하는 부분이 있습니다.
우리는 이 좌표를 지정하기 위해서 현 작업영역의 크기를 알아 둘 필요가 있는것입
니다. 왜 그런지는 아래 부분에 가면 이해가 갈겁니다.
현 작업영역의 크기를 알기 위해서 GetClientRect() 함수를 사용했던거 기억납니
까? 자 그러면 어떤 함수를 이용해서 상태바를 만들수 있는지 봅시다. 상태바는 우
리가 윈도우를 생성하때 사용했던 CreateWindow() 함수를 이용해서 만듭니다. 상태
바를 만들기 위한 새로운 함수가 있는것은 아닙니다. 단지 클래스 이름을 등록하는
부분에 STATUSCLASSNAME이라는 정의된 예약어를 지정하면 됩니다.
hStatebar = CreateWindow(
STATUSCLASSNAME,
"",
WS_CHILD|WS_VISIBLE,
0, 0, 0, 0,
hWnd,
NULL,
hInst,
NULL
);
위와 같은 형식으로 사용하면 되죠. 상태바를 생성하는 함수를 이용해서 생성했으
니 이 상태바의 모양을 결정해야 될겁니다. 먼저 파트를 나누어야 겠죠? 파트를
나눌때에는 상태바에 특수한 메시지를 보냄으로써 할수 있는데 상태바에 특수한
메시지를 어떻게 보낼수 있을까요? SendMessage()라는 함수의 첫번째 파라미터에
상태바 핸들을 지정하면 됩니다. 그런데 우리가 이 SendMessage() 함수를 위에서
배운적이 있나요? 이거 잘 기억이 안나네......... 그럼 알아 보도록 하죠.
유저가 어떤 작업을 하게 되면 항상 메시지가 발생하게 됩니다. 그런데 우리는 이
메시지를 함수를 이용해서 강제적으로 보낼수 있습니다. 예를들어 볼까요? 예를들
어서 유저가 a라는 키를 누르게 되면 어떤 메시지가 보내집니까? 주 메시지인 mesg
에 WM_CHAR가 들어 갈테고 부메시지인 wParam에 'a'라는 문자가 기억되어 있을겁니
다. 그렇죠? 그런데 이 메시지를 강제로 보내는 방법이 있습니다. 다시 말해서 'a'
를 누르지 않아도 똑같은 메시지가 발생될수 있도록 할수 있다는 것입니다.
char cChar = 'a';
SendMessage(hWnd, WM_CHAR, (WPARAM)cChar, (LPARAM)0);
위와 같은 구문으로 보낼수 있습니다. 그런데 위 구문이 맞는지 모르겠네?????
하여간 구문이 맞는지 여부를 따지지 말고 이해만 하면 됩니다. 첫번째 파라미
터는 메시지를 받을 윈도우의 핸들이고 두번째 파라미터는 주 메시지, 세번째,
네번째 파라미터는 부메시지를 지정하면 됩니다. 윈도우즈용 프로그램을 보면 메뉴
아이템중 프로그램 종료하는 것이 있을 겁니다.
SendMessage(hWnd, WM_DESTROY, (WPARAM)0, (LPARAM)0);
메뉴 아이템이 선택되었을때 위 구문을 사용하면 종료가 됩니다. 그렇겠죠?
SendMessage() 함수의 쓰임을 알았으니 계속해서 상태바에 대한 부분을 얘기해 보
도록 하겠습니다. 위에서도 언급했지만 메시지를 상태바에 보냄으로써 파트를 나룰
수 있는데 이때에 SB_SETPARTS라는 메시지를 보냅니다. 부메시지인 wParam에 파트수
와 lParam에 파트를 나눌 좌표를 가지고 있는 배열의 첫주소를 지정하면 됩니다.
예를 하나 들어보죠.
SendMessage(hStatebar, SB_SETPARTS, (WPARAM)3, (LPARAM)statePart);
위의 구문은 상태바를 세 등분으로 나누고 있는 것을 보여 주고 있는 것입니다.
아마 statePart[0]에 첫번째 파트의 오른쪽 좌표, statePart[1]에 두번째 파트의
오른쪽 좌표, statePart[2]에 마지막 좌표값이 들어 있을 겁니다.
파트를 나누었으니 각 파트에 문자열을 지정하는 방법만 알면 되겠네요.
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)0, (LPARAM)"First State Window");
위와 같이 역시 SendMessage() 함수를 이용해서 지정하면 되는데 세번째 파라미
터에 파트의 인덱스를 지정하면 됩니다. 차례로 0부터 증가되는 형식이 되죠. 만
약에 상태바의 어떤 파트에 어떤 문자열이 있는지 알고 싶으면 SB_GETTEXT라는 메
시지를 보내면 알수 있습니다.
char szBuff[100];
SendMessage(hStatebar, SB_GETTEXT, (WPARAM)0, (LPARAM)szBuff);
첫벗째 파트에 현재 어떤 문자열이 지정되어 있는지 알수 있게 해주는 구문입니다.
자 그러면 간단하게 구현한 프로그램을 보도록 합시다. 이 예제는 상태바의 파트를
세개로 나누어서 마지막 파트에 시간이 출력되도록 한것입니다. 윈도우즈용 프로그
램을 보면 이런형식을 띄고 있는 것이 많을 겁니다. 그러면 소스를 보도록 합시다.
먼저 리소스 파일의 소스입니다.
#include <windows.h>
MyMenu MENU
BEGIN
POPUP "&Statebar"
BEGIN
MENUITEM "&Hide", 100
MENUITEM "&Show", 200
END
END
프로그램 소스입니다.
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateSTATEBAR(HWND, HINSTANCE);
char* TimeCal(void);
HWND hStatebar;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create statebar";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = "MyMenu";
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
InitCommonControls();
SetTimer(hWnd, 300, 1000, NULL);
CreateSTATEBAR(hWnd, hInstance);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
switch(mesg)
{
case WM_COMMAND :
switch(LOWORD(wParam))
{
case 100 :
ShowWindow(hStatebar, SW_HIDE);
break;
case 200 :
ShowWindow(hStatebar, SW_RESTORE);
break;
}
return FALSE;
case WM_TIMER :
if(LOWORD(wParam) == 300)
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)2,
(LPARAM)TimeCal());
return FALSE;
case WM_DESTROY :
KillTimer(hWnd, 300);
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
char* TimeCal(void)
{
static int i;
static struct tm *newtime;
static time_t t;
static char cTime[50];
static char *Total;
t = time(NULL);
newtime = localtime(&t);
strcpy(cTime, asctime(newtime));
cTime[strlen(cTime)-1] = '\0';
Total = strtok(cTime, " ");
for(i=0; i<3; i++)
Total = strtok(NULL, " ");
return Total;
}
void CreateSTATEBAR(HWND hWnd, HINSTANCE hInst)
{
RECT rect;
int statePart[3], n;
GetClientRect(hWnd, &rect);
for(n=1; n<=3; n++)
statePart[n-1] = rect.right/3*n;
hStatebar = CreateWindow(
STATUSCLASSNAME,
"",
WS_CHILD|WS_VISIBLE,
0, 0, 0, 0,
hWnd,
NULL,
hInst,
NULL
);
SendMessage(hStatebar, SB_SETPARTS, (WPARAM)3, (LPARAM)statePart);
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)0,
(LPARAM)"First State Window");
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)1,
(LPARAM)"Second State Window");
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)2, (LPARAM)TimeCal());
}
자 그러면 구체적으로 분석해 봅시다.
InitCommonControls();
SetTimer(hWnd, 300, 1000, NULL);
CreateSTATEBAR(hWnd, hInstance);
시간을 매초마다 갱신하기 위해서 타이머를 걸어 두었군요. 그리고 필자가 만든
함수가 수행되는데 어떤일을 하는지 봅시다.
void CreateSTATEBAR(HWND hWnd, HINSTANCE hInst)
{
RECT rect;
int statePart[3], n;
GetClientRect(hWnd, &rect);
for(n=1; n<=3; n++)
statePart[n-1] = rect.right/3*n;
현재 작업영역의 크기를 이용해서 똑같이 세부분으로 확인하는 과정입니다.
hStatebar = CreateWindow(
STATUSCLASSNAME,
"",
WS_CHILD|WS_VISIBLE,
0, 0, 0, 0,
hWnd,
NULL,
hInst,
NULL
);
클래스 이름만 다르게 하고 상태바를 생성하는군요. 이미 앞에서 설명을 드린 부분
입니다.
SendMessage(hStatebar, SB_SETPARTS, (WPARAM)3, (LPARAM)statePart);
지정해 준 좌표를 이용해서 세파트로 나누고 있는 부분입니다.
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)0,
(LPARAM)"First State Window");
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)1,
(LPARAM)"Second State Window");
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)2,
(LPARAM)TimeCal());
}
각 파트에 문자열을 지정하고 있습니다. TimeCal()이라는 함수는 이미 우리가
타이머를 배울때 사용했던 함수죠. 잘 기억이 나지 않는다면 앞 부분을 다시
보시기 바랍니다.
case WM_TIMER :
if(LOWORD(wParam) == 300)
SendMessage(hStatebar, SB_SETTEXT, (WPARAM)2, (LPARAM)TimeCal());
return FALSE;
매 초가 되면 새로운 시간을 갱신해서 세번째 파트에 시간을 지정해 주고 있습니
다.
case WM_COMMAND :
switch(LOWORD(wParam))
{
case 100 :
ShowWindow(hStatebar, SW_HIDE);
break;
case 200 :
ShowWindow(hStatebar, SW_RESTORE);
break;
}
return FALSE;
툴바를 보이고 하고 보이지 않게 했던 구문과 똑같죠? 역시 상태바를 보이게 하고
보이지 않게 하는 역할을 하고 있는 것입니다.
그런데 중요한 설명을 빠뜨리고 설명을 드린것 같군요. 툴바때도 그렇고 상태바를
구현할때에도 마찬가지로 컴파일할때 comctl32.lib파일을 같이 묶어준 상태에서
컴파일을 해주어야 합니다. 다시 말해서 commctrl.h 헤더 파일을 이용해서 프로그
램을 작성하고 나서는 꼭 comctl32.lib 파일을 묶어서 컴파일 해야 한다는 얘기입
니다. 만약에 그렇지 않으면 에러가 발생할 겁니다. 이 파일은 비주얼C가 인스톨
되어 있는 디렉토리내의 lib 디렉토리내에 있습니다.
반응형

'API 관련 > API 강좌모음' 카테고리의 다른 글

[강좌 11] 그래픽2  (0) 2007.10.26
[강좌 10] 그래픽1  (0) 2007.10.26
[강좌 8] 메뉴1  (0) 2007.10.26
[강좌 7] 마우스, 타이머  (0) 2007.10.26
[강좌 6] 출력부분  (0) 2007.10.26
Comments