[WindowsAPI] 6주차, 선그리기 오류수정과 radio&listBox 다루기


교내 전공과목인 윈도우즈 API수업을 정리합니다

 

 

 

1. Mouse 파일의 BackSpace 동작의 오류를 고치자

(1) 오류 화면

선을 몇개 그리다가 지우기(BackSpace)동작을 반복하면 선이 이어지는 오류가 발생한다.

이는 소스코드에서 선 객체를 셀 때 초기화 동작을 제대로 해 주지 않은 것이 원인이다.

 

(2) 소스코드

// 선을 지울 때, 혹은 선을 그릴 때를 선택하여 현재 객체의 가진 점 개수를 초기화 시키도록 하자.

/* WnProc > WM_KEYDOWN */
	case WM_KEYDOWN: 
		switch (wParam) {
		case VK_BACK: // 선 객체 지우기
			if (iLineCount > 0) {
				iLineCount--;
				lines[iLineCount].iPointCount = 0; // 초기화ver1 : 백스페이스를 누를 때마다 카운트를 초기화시키기
				InvalidateRect(hWnd, NULL, TRUE);
			}
			break;
		}
		return 0;
        
        
/* WnProc > WM_LBUTTONDOWN */
	case WM_LBUTTONDOWN: // 선 그리기 시작
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		//	bNowDraw=TRUE;

		// lines[iLineCount].iPointCount = 0;   // 초기화.ver2 : 왼쪽버튼을 누를 때마다 카운트 초기화하기


		/* 구조체에 선 정보 (시작점) 저장하기 */
		lines[iLineCount].p[lines[iLineCount].iPointCount].x = x;
		lines[iLineCount].p[lines[iLineCount].iPointCount].y = y;
		//lines[iLineCount].iPointCount++;

		lines[iLineCount].penColor = CurrentPenColor;

		return 0;

 

(3) 실행결과

정상작동함. 사실 아님. 10번중 1번 꼴로 오류가 발생하는데 우선 배운 내용을 한 바퀴 돌고 생각해보자.

 

 

2. 선 색상이 날라가는 문제가 있다. 고치자

(1) 오류사항

선을 그리고, 선을 그려낸 윈도우 창을 흔들다 보면 선 객체들의 색상이 날라가는 문제가 발생한다.

우선 윈도우를 흔들어서 나타나는 문제이므로 윈도우에 다시 객체를 생성할 때(WM_PAINT) 발생하는 원인일 것이다.

그리고 색상에서 문제가 일어났으니 hPen관련 설정이 덜 된 것이다.

 

(2) 해결 소스코드

	case WM_PAINT: // 저장된 선의 정보 그리기 실행
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLineCount; i++) {

			hPen = CreatePen(PS_SOLID, 1, lines[i].penColor);
            
			hPen = (HPEN)SelectObject(hdc, hPen); // 펜 객체 선택

			MoveToEx(hdc, lines[i].p[0].x, lines[i].p[0].y, NULL);
			for (int j = 1; j < lines[i].iPointCount; j++) {
				LineTo(hdc, lines[i].p[j].x, lines[i].p[j].y);
			}
			DeleteObject(SelectObject(hdc, hPen)); //생성한 것은 없애라. 삭제했음.
		}
		EndPaint(hWnd, &ps);
		return 0;

생성한 펜은 지우는 것까지 확실히 해주도록 했다. (SelectObject -> DeleteObject)

 

(3). 실행결과

아무리 윈도우 창을 흔들어도 WM_PAINT에서 발생했던 색이 날라가는 현상은 일어나지 않는다. 오류 고치기 완료.

 

 

 

3. Radio버튼을 통해 도형의 형태와 색상을 제어하자

(1) 소스코드

/* radio를 위한 전역변수 */
enum { ID_R1 = 101, ID_R2, ID_R3};
HWND r1, r2, r3;
/* radio를 위한 전역변수 */

// WinProc > WM_CREATE
	/* radio를 위한 코드 */
	case WM_CREATE: 
		CreateWindow(TEXT("button"), TEXT("Color"), WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 5, 5, 120, 110, hWnd, (HMENU)1, 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 | WS_GROUP, 10, 50, 100, 30, hWnd, (HMENU)ID_R2, g_hInst, NULL);
		r3 = CreateWindow(TEXT("button"), TEXT("BLUE"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 10, 80, 100, 30, hWnd, (HMENU)ID_R3, g_hInst, NULL);
		CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);// 
		break;
	/* radio를 위한 코드 */
    
//Winproc > WM_INITMENU:
	case WM_INITMENU: //메뉴바(선색깔)에 현재 선택된 것을 체크표시하도록 하자. (radio버튼과의 동기화)
		if (CurrentPenColor == 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 (CurrentPenColor == 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 (CurrentPenColor == 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;

//WinProc > WM_COMMAND
	case WM_COMMAND: //메뉴바 적용하기
		switch (LOWORD(wParam)) {
		case IDM_RED:
		case ID_R1:
			CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);
			CurrentPenColor = RGB(255, 0, 0);
			SetFocus(hWnd);
			break;
		case IDM_GREEN:
		case ID_R2:
			CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R2);
			CurrentPenColor = RGB(0, 255, 0);
			SetFocus(hWnd);
			break;
		case IDM_BLUE:
		case ID_R3:
			CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R3);
			CurrentPenColor = RGB(0, 0, 255);
			SetFocus(hWnd);
			break;
		}
		return 0;
  •  CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HANDLE hInstance, LPVOID lpParam);
    윈도우를 생성함.
    - lpClassName : 생성할 윈도우의 윈도우 클래스를 지정하는 문자열이다. 윈도우 클래스는 RegisterClass(Ex) 함수로 직접 등록할 수도 있고 또는 button, edit, listbox, static 등과 같이 미리 정의되어 있는 시스템 전역 클래스일 수도 있다. 또는 GlobalAddAtom 함수로 미리 등록해 놓은 정수형의 아톰을 사용하는 것도 가능하다.
    - lpWindowName : 윈도우의 타이틀 바에 나타날 캡션 문자열이다. 캡션이 나타날 위치는 윈도우 클래스에 따라 달라지는데 오버랩드 윈도우는 타이틀 바에 캡션이 나타나며 버튼, 스태틱 등의 컨트롤은 컨트롤 중앙에 캡션이 출력된다.
    - dwStyle : 생성될 윈도우의 스타일을 지정한다.
    - x, y: 위치
    - nWidth, nHeight: 크기
    - hWndParent: 부모 윈도우, 또는 소유주 윈도우의 핸들을 지정한다
    - hMenu: 오버랩드 윈도우 혹은 팝업 윈도우의 경우 메뉴의 핸들을 지정한다
    - hInstance: 해당 윈도우를 생성하는 인스턴스 핸들을 지정한다. (이 인스턴스 종료시 윈도우도 같이 파괴됨)
    - lpParam: CREATESTRUCT 구조체의 포인터
  • CheckRadioButton( HWND hDlg, int nIDFirstButton, int nIDLastButton, int nIDCheckButton );
    대화상자에 배치된 라디오 버튼 그룹 중 하나의 라디오 버튼을 체크한다.  (나머지는 해제)
    - hDlg : 라디오 버튼을 가지고 있는 윈도우 핸들
    - nIDFirstButton : 그룹 내의 첫번째 라디오 버튼 ID
    - nIDLastButton : 그룹 내의 마지막 라디오 버튼 ID
    - nIDCheckButton : 체크할 라디오 버튼의 ID
  • SetFocus(HWND hWnd);
    보통 사용자가 컨트롤을 선택하여 포커스 이동시키는데, 이 함수를 통해 강제로 이동시킬 수 있다.
    - hWnd : 포커스를 가질 윈도우의 핸들. NULL일 경우 모든 키보드 입력을 무시한다.

(2) 실행화면

 

 

4. 선굵기를 조절하는 리스트박스를 만들자

(1). 소스코드

typedef struct _line {
	POINT p[1000];
	int iPointCount;
	COLORREF penColor; 
	int penWidth; // 구조체에 선 굵기 속성 추가
} line;  

enum { ID_R1 = 101, ID_R2, ID_R3, ID_LISTBOX }; // listBox를 위한 ID_LISTBOX추가

/* listBox(선굵기)를 위한 전역변수 */
int iCurrentPenWidth = 8;
HWND hList;
TCHAR* Items[] = { TEXT("2"), TEXT("4"), TEXT("6"), TEXT("8") };

// WndProc
	int i; //선택된 list값을 위한 지역변수, WM_CREATE && WM_CAMMAND 에서 사용됨.

// WndProc > WM_CREATE
	case WM_CREATE: 
		CreateWindow(TEXT("button"), TEXT("Color"), WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 5, 5, 120, 110, hWnd, (HMENU)1, 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 | WS_GROUP, 10, 50, 100, 30, hWnd, (HMENU)ID_R2, g_hInst, NULL);
		r3 = CreateWindow(TEXT("button"), TEXT("BLUE"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP, 10, 80, 100, 30, hWnd, (HMENU)ID_R3, g_hInst, NULL);
		CheckRadioButton(hWnd, ID_R1, ID_R3, ID_R1);// (hWnd, 그룹내 첫번째 라디오 버튼, 그룹 내 마지막 라디오 버튼, 체크할 라디오 버튼)

	/* LIstBox를 위한 코드 */
		hList = CreateWindow(TEXT("listbox"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | LBS_NOTIFY, 150, 10, 100, 100, hWnd, (HMENU)ID_LISTBOX, g_hInst, NULL);
		for ( i = 0; i < 4; i++) {
			SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)Items[i]);
		}
		SendMessage(hList, LB_SETCURSEL, iCurrentPenWidth / 2 - 1, 0);
		return 0;

// WndProc > WM_COMMAND
		case ID_LISTBOX: //사용자가 listBox를 조작한다면
			switch (HIWORD(wParam)) {
			case LBN_SELCHANGE:
				i = SendMessage(hList, LB_GETCURSEL, 0, 0); // i는 선택된 item의 index
				iCurrentPenWidth = (i + 1) * 2;
				SetFocus(hWnd);
				break;
			}
            
// WndProc > WM_LBUTTONDOWN
		lines[iLineCount].penWidth = iCurrentPenWidth; //선 굵기 지정

// WndProc > WM_MOUSEMOVE > if문 안에
			hPen = CreatePen(PS_SOLID, lines[iLineCount].penWidth, lines[iLineCount].penColor); // 크기, 색상 조정
            
            
// WndProc > WM_PAINT > for문 안에
			hPen = CreatePen(PS_SOLID, lines[i].penWidth, lines[i].penColor);

(2). 실행화면

 

 

 

다음은 대화상자 다루기로 중간범위 완결.