반응형

컨트롤

컨트롤이란 사용자와 인터페이스를 이루는 도구(작은 윈도우)이다.
컨트롤의 의미를 한마디로 설명하기는 힘들지만 "버튼, 에디트, 리스트 박스, 스크롤 바 등을 뭉뚱그려 컨트롤이라고 한다"

대표적 컨트롤
윈도우 클래스 컨트롤
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));
}

콤보박스를 만드는 것은 리스트 박스와 비슷하다.

비슷한 구조에서 컨트롤 선언, 스타일, 메시지 등만 바꿔주면 된다. 

 

반응형

+ Recent posts