컨트롤
컨트롤이란 사용자와 인터페이스를 이루는 도구(작은 윈도우)이다.
컨트롤의 의미를 한마디로 설명하기는 힘들지만 "버튼, 에디트, 리스트 박스, 스크롤 바 등을 뭉뚱그려 컨트롤이라고 한다"
대표적 컨트롤 | |
윈도우 클래스 | 컨트롤 |
button | 버튼, 체크, 라디오 |
static | 텍스트 |
scrollbar | 스크롤 바 |
edit | 에디트 |
listbox | 리스트 박스 |
combobox | 콤보 박스 |
CreateWindow 함수의 첫 번째 인수를 클래스 이름만 주면 해당 컨트롤을 만들 수 있다.
Button
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch (iMessage) {
case WM_CREATE:
CreateWindow(TEXT("button"),TEXT("Click Me"),WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,20,20,100,25,hWnd,(HMENU)0,g_hInst,NULL);
CreateWindow(TEXT("button"),TEXT("Me Two"),WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON,20,50,100,25,hWnd,(HMENU)1,g_hInst,NULL);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {//컨트롤 ID
case 0:
MessageBox(hWnd,TEXT("First Button Clicked"),TEXT("Button"),MB_OK);
break;
case 1:
MessageBox(hWnd,TEXT("Second Button Clicked"),TEXT("Button"),MB_OK);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
WM_CREATE
1, 2. CreateWindow( 1. TEXT("button"), 2. TEXT("Click Me"), 3. WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON, 4. 20, 5. 20, 6. 100, 7. 25, 8. hWnd, 9. (HMENU)0, 10. g_hInst,NULL);
1. "button" 윈도우 클래스명
2. "Click Me" 버튼에 나타나는 텍스트
3. 세 번째 인수는 윈도우 속성 값이다. WS_CHILD함수는 필수로 주고 WS_VISIBLE 스타일을 주어야 ShowWindow 함수 없이 컨트롤이 화면에 나온다. 그 외에 컨트롤에 고유한 스타일을 추가한다. 버튼의 스타일의 값은 BS이다.
여기서는 푸시 버튼 BS_PUSHBUTTON을 지정했다.
4~7. 까지 인수는 모두 위치이다. 4=x, 5=y, 6=폭 7=높이
8. 부모 윈도를 지정한다. 이 프로그램의 부모는 메인 윈도우 hWnd이다.
9. 윈도우의 ID이다.
10. 인스턴스 핸들 hlnstance의 사본인 g_hlnst를 받는다.
WM_COMMAND
LOWORD(wParam)을 통해 위에서 지정한 ID를 받아온다
MessageBox(hWnd,TEXT("First Button Clicked"),TEXT("Button"),MB_OK);
"Fisrt Button Clicked" 메시지 박스에서 출력하는 메시지
"Button" 메시지 박스 캡션
MB_OK 확인 키
라디오 버튼
V.Radio Button
typedef struct _line {
POINT p[500];
int iCount;
COLORREF pColor;
} line;
line lines[1000];
int iLines;
enum { ID_R1, ID_R2, ID_R3};//라디오 버튼아이디
HWND r1, r2, r3; //라디오 버튼 핸들
COLORREF iCurrentPenColor = RGB(255, 0, 0);
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static int x;
static int y;
//static BOOL bNowDraw=FALSE;
HPEN hPen;
switch (iMessage) {
case WM_CREATE://윈도우 생성되기 직전에 한번만 실행되는 메시지
CreateWindow(TEXT("button"), TEXT("Color"), WS_CHILD | WS_VISIBLE |
BS_GROUPBOX/*테두리*/, 5, 5, 120, 110, hWnd, (HMENU)0, g_hInst, NULL);
r1 = CreateWindow(TEXT("button"), TEXT("Red"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON | WS_GROUP,
10, 20, 100, 30, hWnd, (HMENU)ID_R1, g_hInst, NULL);
r2 = CreateWindow(TEXT("button"), TEXT("Green"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON,
10, 50, 100, 30, hWnd, (HMENU)ID_R2, g_hInst, NULL);
r3 = CreateWindow(TEXT("button"), TEXT("Blue"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON,
10, 80, 100, 30, hWnd, (HMENU)ID_R3, g_hInst, NULL);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)){
case IDM_RED:
case ID_R1:
iCurrentPenColor = RGB(255, 0, 0);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
break;
case IDM_GREEN:
case ID_R2:
iCurrentPenColor = RGB(0, 255, 0);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R2);
break;
case IDM_BLUE:
case ID_R3:
iCurrentPenColor = RGB(0, 0, 255);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R3);
break;
}
return 0;
case WM_INITMENU:
if (iCurrentPenColor == RGB(255, 0, 0)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_UNCHECKED);
}
else if (iCurrentPenColor == RGB(0, 255, 0)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_UNCHECKED);
}
else if (iCurrentPenColor == RGB(0, 0, 255)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_CHECKED);
}
return 0;
case WM_KEYDOWN:
switch (wParam) {
case VK_BACK:
if(iLines>0)
iLines--;
InvalidateRect(hWnd, NULL, TRUE);
break;
}
return 0;
case WM_LBUTTONDOWN:
x=LOWORD(lParam);
y=HIWORD(lParam);
lines[iLines].iCount = 0;
lines[iLines].pColor = iCurrentPenColor;
lines[iLines].p[lines[iLines].iCount].x = x;
lines[iLines].p[lines[iLines].iCount].y = y;
lines[iLines].iCount++;
return 0;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON) {
hdc=GetDC(hWnd);
hPen = CreatePen(PS_SOLID, 3, iCurrentPenColor);
hPen=(HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc,x,y,NULL);
x=LOWORD(lParam);
y=HIWORD(lParam);
lines[iLines].p[lines[iLines].iCount].x = x;
lines[iLines].p[lines[iLines].iCount].y = y;
lines[iLines].iCount++;
LineTo(hdc,x,y);
DeleteObject(SelectObject(hdc, hPen));
ReleaseDC(hWnd,hdc);
}
return 0;
case WM_LBUTTONUP:
iLines++;
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for (int i = 0; i < iLines; i++)
{
hPen = CreatePen(PS_SOLID, 3, lines[i].pColor);
hPen = (HPEN)SelectObject(hdc, hPen);
MoveToEx(hdc, lines[i].p[0].x,
lines[i].p[0].y, NULL);
for (int j = 0; j < lines[i].iCount; j++)
{
LineTo(hdc, lines[i].p[j].x,
lines[i].p[j].y);
}
DeleteObject(SelectObject(hdc, hPen));
}
return 0;
case WM_LBUTTONDBLCLK:
InvalidateRect(hWnd, NULL, TRUE);
return 0;
case WM_RBUTTONDOWN:
SetFocus(hWnd); //작업 영역을 메인 윈도우로 바꾼다
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
전역 변수 자료구조
enum { ID_R1, ID_R2, ID_R3};//라디오 버튼아이디
HWND r1, r2, r3; //라디오 버튼 핸들
WM_CREATA
case WM_CREATE://윈도우 생성되기 직전에 한번만 실행되는 메시지
CreateWindow(TEXT("button"), TEXT("Color"), WS_CHILD | WS_VISIBLE |
BS_GROUPBOX/*테두리*/, 5, 5, 120, 110, hWnd, (HMENU)0, g_hInst, NULL);
r1 = CreateWindow(TEXT("button"), TEXT("Red"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON | WS_GROUP,
10, 20, 100, 30, hWnd, (HMENU)ID_R1, g_hInst, NULL);
r2 = CreateWindow(TEXT("button"), TEXT("Green"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON,
10, 50, 100, 30, hWnd, (HMENU)ID_R2, g_hInst, NULL);
r3 = CreateWindow(TEXT("button"), TEXT("Blue"), WS_CHILD | WS_VISIBLE |
BS_AUTORADIOBUTTON,
10, 80, 100, 30, hWnd, (HMENU)ID_R3, g_hInst, NULL);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
return 0;
원도우 생성되기 직전에 한 번만 실행되는 메시지
1. CreateWindow(TEXT("button"), TEXT("Color"), WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 5, 5, 120, 110, hWnd, (HMENU)0, g_hInst, NULL)는 테두리를 만드는 함수 BS_GROUPBOX는 테두리를 그린다
2. r1 = CreateWindow(TEXT("button"), TEXT("Red"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 10, 20, 100, 30, hWnd, (HMENU)ID_R1, g_hInst, NULL)윈도우 핸들에 저장한다. 이 프로그램에서는 꼭 저장할 필요는 없다. BS_AUTORADIOBUTTON 스타일은 자동 라디오 버튼이다.
WM_COMMAND
case WM_COMMAND:
switch (LOWORD(wParam)){
case IDM_RED:
case ID_R1:
iCurrentPenColor = RGB(255, 0, 0);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
break;
case IDM_GREEN:
case ID_R2:
iCurrentPenColor = RGB(0, 255, 0);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R2);
break;
case IDM_BLUE:
case ID_R3:
iCurrentPenColor = RGB(0, 0, 255);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R3);
break;
}
return 0;
차일드 컨트롤은 WM_COMMAND를 통해 부모 윈도에게 메시지를 보낸다.
1. LOWORD(wParam)은 ID값을 보내준다.
2. 선택한 ID가 IDM_RED or ID_R1이면 iCurrentPenColor의 색상은 빨간 라디오 버튼은 ID_R1이 설정된다.
WM_INITMENU
case WM_INITMENU:
if (iCurrentPenColor == RGB(255, 0, 0)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_UNCHECKED);
}
else if (iCurrentPenColor == RGB(0, 255, 0)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_CHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_UNCHECKED);
}
else if (iCurrentPenColor == RGB(0, 0, 255)){
CheckMenuItem((HMENU)wParam, IDM_RED, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_GREEN, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem((HMENU)wParam, IDM_BLUE, MF_BYCOMMAND | MF_CHECKED);
}
return 0;
메뉴를 클릭했을 때
- iCurrentPenColor의 색상이 빨강이면 메뉴에 빨강 버튼을 체크/ 파랑 언체크/초록 언체크
백스페이스가 먹히는가?
라디오 버튼을 클릭하고 백스페이스바를 누르면 선이 지워지지 않는다.
그 이유는 위에서 말한 것처럼 컨트롤들도 하나의 작은 윈도우기 때문에 라디오라는 윈도우에 백스페이스 키다운 명령을 보내고 있기 때문이다.
라디오 박스로 포커스 돼 있는걸 메인 윈도우창으로 다시 가져와야 한다.
WM_RBUTTONDOWN
case WM_RBUTTONDOWN:
SetFocus(hWnd); //작업 영역을 메인 윈도우로 바꾼다
return 0;
SetFocus 함수는 괄호 안의 영역으로 바꿔준다. 우리는 메인 윈도우로 이동해야 되기 때문에 hWnd를 넣는다.
WM_RBUTTONDOWN에 넣는 게 아니라
switch (LOWORD(wParam)){
case IDM_RED:
case ID_R1:
iCurrentPenColor = RGB(255, 0, 0);
CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
SetFocus(hWnd); //작업 영역을 메인 윈도우로 바꾼다
break;
WM_COMMAND에서 바로 빨간색을 선택하자마자 작업 영역을 바로 윈도우로 바꿔 줄 수도 있다.
ListBox
#define ID_LISTBOX 100
TCHAR *Items[] = { TEXT("Apple"), TEXT("Orange"), TEXT("Melon"),
TEXT("Grape"), TEXT("Strawberry") };
HWND hList;
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
int i;
TCHAR str[128];
switch (iMessage) {
case WM_CREATE:
hList=CreateWindow(TEXT("listbox"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
LBS_NOTIFY,10,10,100,200,hWnd,(HMENU)ID_LISTBOX,g_hInst,NULL);
for (i=0;i<5;i++) {
SendMessage(hList,LB_ADDSTRING/*문자열의 번지를 넘겨준다*/,0,(LPARAM)Items[i]);
}
SendMessage(hList, LB_SETCURSEL, 0, (LPARAM)Items[i]);//선택한 디폴드 값
//SendMessage(hList, LB_GETTEXT, i, (LPARAM)str);
//SetWindowText(hWnd, str); //윈도우 caption에 출력
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(ID_LISTBOX, LBN_SELCHANGE)
/*커맨드에서 listbox와 selchange선택*/, 0);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {//LOWORD면 어떤 친구가 때렸지는지 어디서이 명령이 왔는지
case ID_LISTBOX:
switch (HIWORD(wParam)) { //HIWORD 구체적 어디에 맞았는지 어떤 상황 (통지 메시지)
case LBN_SELCHANGE:
i=SendMessage(hList, LB_GETCURSEL,0,0);//현재 선택중인 리스트 박스의
인덱스를 리턴
SendMessage(hList, LB_GETTEXT, i, (LPARAM)str);
SetWindowText(hWnd, str); //윈도우 caption에 출력
break;
}
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
자료구조
#define ID_LISTBOX 100//ID값 저장된공간
TCHAR *Items[] = { TEXT("Apple"), TEXT("Orange"), TEXT("Melon"),
TEXT("Grape"), TEXT("Strawberry") };
HWND hList;
Items=문자열 변수
hList=윈도우 핸들을 넣는다.
WM_CREATE
case WM_CREATE:
hList=CreateWindow(TEXT("listbox"),NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |
LBS_NOTIFY,10,10,100,200,hWnd,(HMENU)ID_LISTBOX,g_hInst,NULL);
for (i=0;i<5;i++) {
SendMessage(hList,LB_ADDSTRING/*문자열의 번지를 넘겨준다*/,0,(LPARAM)Items[i]);
}
SendMessage(hList, LB_SETCURSEL, 0, (LPARAM)Items[i]);//선택한 디폴드 값
//SendMessage(hList, LB_GETTEXT, i, (LPARAM)str);
//SetWindowText(hWnd, str); //윈도우 caption에 출력
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(ID_LISTBOX, LBN_SELCHANGE), 0);
return 0;
1. hList핸들은 listbox 컨트롤을 가진다.
2. 리스트박스에 Items[0]의 텍스트를 추가 Items[4]까지 반복
3. 처음 시작할 때의 기본 값
4. MAKEPARAM(리스트박스, 사용자에 의해 선택)WM_COMMAND에 인수 강제 전달
WM_COMMAND
case WM_COMMAND:
switch (LOWORD(wParam)) {//LOWORD면 어떤 친구가 때렸지는지 어디서이 명령이 왔는지
case ID_LISTBOX:
switch (HIWORD(wParam)) { //HIWORD 구체적 어디에 맞았는지 어떤 상황 (통지 메시지)
case LBN_SELCHANGE://1
i=SendMessage(hList, LB_GETCURSEL,0,0);//2현재 선택중인 리스트 박스의 인덱스를
리턴
SendMessage(hList, LB_GETTEXT, i, (LPARAM)str);//3
SetWindowText(hWnd, str); //4 윈도우 caption에 출력
break;
}
}
인수 | 설명 |
HIWORD(wParam) | 통지 코드 |
LOWORD(wParam) | 컨트롤의 ID |
lParam | 메시지를 보낸 차일드 윈도우의 윈도우 핸들 |
통지 코드란 Listbox가 어떤 상황인지 설명해주는 코드이다. 다시 말해 차일드 컨트롤이 왜 메시지를 보냈는가를 나타내는 값이다.
1. 사용자에 의해 선택이 변경되어 메세지를 보냈다
2. 변수 i에 현재 선택된 인덱스를 넣는다
3. 선택되서 읽어온 인덱스의 값을 str문자열에 넣는다.
4. 윈도우캡션을 str문자열로 바꾼다.
COMBOBOX
#define ID_COMBOBOX 100
TCHAR *Items[]={TEXT("Apple"),TEXT("Orange"),TEXT("Melon"),TEXT("Grape"),
TEXT("Strawberry")};
HWND hCombo;
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
int i;
TCHAR str[128];
switch (iMessage) {
case WM_CREATE:
hCombo=CreateWindow(TEXT("combobox"),NULL,WS_CHILD | WS_VISIBLE |
CBS_DROPDOWN,10,10,100,200,hWnd,(HMENU)ID_COMBOBOX,g_hInst,NULL);
for (i=0;i<5;i++) {
SendMessage(hCombo,CB_ADDSTRING,0,(LPARAM)Items[i]);
}
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_COMBOBOX:
switch (HIWORD(wParam)) {
case CBN_SELCHANGE:
i=SendMessage(hCombo, CB_GETCURSEL,0,0);
SendMessage(hCombo, CB_GETLBTEXT, i, (LPARAM)str);
SetWindowText(hWnd, str);
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd,str);
break;
}
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
콤보박스를 만드는 것은 리스트 박스와 비슷하다.
비슷한 구조에서 컨트롤 선언, 스타일, 메시지 등만 바꿔주면 된다.
'학교수업 > 윈도우즈 프로그래밍' 카테고리의 다른 글
7주차 - 유니코드&MBCS / 대화상자 / 스크롤바 맛보기 (0) | 2021.01.27 |
---|---|
선그리기 v.combo (0) | 2021.01.26 |
5주차 - 펜 스타일 지정 /oldPen/ 선하나 지우기/리소스 메뉴 (0) | 2021.01.25 |
4주차 - KeyDown / static 변수 / UINCODE&MultiByte /선그리기 (0) | 2021.01.24 |
3주차 - 복습 / struct와 class로 1, 2주차 프로그램 만들기 (0) | 2021.01.21 |