[windowsAPI] 5주차, 마우스이벤트(그림판) 다루기

 

 

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

 

 

 

1. Mouse 버튼을 통해 선그리기를 해보자

(1) 소스코드

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	static int x;
	static int y;
	//static BOOL bNowDraw=FALSE;

	switch (iMessage) {
	case WM_LBUTTONDOWN:
		/* 선그리기 시작을 알림 */
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		//	bNowDraw=TRUE;
		return 0;
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {  // & = 비트연산자  // C언어에서 0 = False, 0이외의 수 = True
			hdc = GetDC(hWnd); 
			MoveToEx(hdc, x, y, NULL);
			x = LOWORD(lParam);
			y = HIWORD(lParam);
			LineTo(hdc, x, y);
			ReleaseDC(hWnd, hdc);
		}
		return 0;
	case WM_LBUTTONUP:
		//	bNowDraw=FALSE;
		return 0;
	case WM_LBUTTONDBLCLK:
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

함수설명은 2번에서.

 

(2)실행화면

 

(3) 문제발생

창을 내렸다가 올리면 초기화가 되는 문제 발생

구조체를 이용하여 초기화 되지 않도록 만들어주자.

 

 

2. 창 초기화가 일어나지 않는 선그리기 프로그램 만들기

(0). 구현사항

1주차의 사각형 그리기에서도 구조체(has 점, 점 수)를 활용하여 방지해주었었다.

이와 같은 원리로 해결하자, 또한 백스페이스를 누르면 선 객체를 지울 수 있도록 만들자.

(1). 소스코드

typedef struct _line{
	POINT p[1000];
	int iPointCount;
} line;  //선 객체 한 개의 표현

line lines[500];
int iLineCount; // 선 객체의 총 수
int iTempPointCount; // 한 개의 선 객체 내의 점개수 카운트


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;

	switch (iMessage) {
	case WM_KEYDOWN:  // 선 객체 지우기
		switch(wParam) {
		case VK_BACK:
			if (iLineCount > 0) {
				iLineCount--;
				InvalidateRect(hWnd, NULL, TRUE);
			}
			break;
		}
		return 0;
	case WM_LBUTTONDOWN: // 선 그리기 시작
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		//	bNowDraw=TRUE;

		/* 구조체에 선 정보 (시작점) 저장하기 */
		lines[iLineCount].p[iTempPointCount].x = x;
		lines[iLineCount].p[iTempPointCount].y = y;
		iTempPointCount++;
		return 0;
	case WM_MOUSEMOVE:
		if (wParam & MK_LBUTTON) {  // & = 비트연산자  // C언어에서 0 = False, 0이외의 수 = True
			// MK_LBUTTON   0x0001 ->00000....0001
			// MK_RBUTTON   0x0002 ->00000....0010 

			hdc = GetDC(hWnd); 
			MoveToEx(hdc, x, y, NULL);
			x = LOWORD(lParam);
			y = HIWORD(lParam);
			LineTo(hdc, x, y);
			ReleaseDC(hWnd, hdc);

			/* 선 객체 정보 저장 (움직이는 점) */
			ReleaseDC(hWnd, hdc);
			lines[iLineCount].p[iTempPointCount].x = x;
			lines[iLineCount].p[iTempPointCount].y = y;
			iTempPointCount++;
		}
		return 0;
	case WM_LBUTTONUP:
		//	bNowDraw=FALSE;
		/* 선 객체의 정보 저장 ( 끝 점) */
		lines[iLineCount].iPointCount = iTempPointCount;
		iLineCount++; // 선 하나 증가
		iTempPointCount = 0;  // 초기화
		return 0;
	case WM_LBUTTONDBLCLK:
		InvalidateRect(hWnd, NULL, TRUE);
		return 0;
	case WM_PAINT: // 저장된 선의 정보 그리기 실행
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLineCount; i++){
			for (int j = 0; j < lines[i].iPointCount - 1; j++){
				MoveToEx(hdc, lines[i].p[j].x, lines[i].p[j].y, NULL);
				LineTo(hdc, lines[i].p[j + 1].x, lines[i].p[j + 1].y);
			}
		}
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
  • WM_LBUTTONDBLCLK : 마우스 더블왼쪽클릭했을 때
    더블클릭으로 인정하는 클릭 간격은 시스템에 정의되어 있다.
  • GetDC( HWND hWnd); 
    - hWnd: 구하고자 하는 윈도우 핸들, NULL일 경우 전체 화면에 대한 DC가 구해짐
    * WM_PAINT 메시지 내에서는 BeginPaint, EndPaint 함수쌍 이용
    * WM_PAINT 메시지 외에서는 GetDC, ReleaseDC 함수쌍을 이용
  • ReleaseDC(HWND hWnd, HDC hDC);
    getDC로 구한 커먼 DC의 핸들은 반드시 이 함수로 해제해 주어야 한다.
    - hWnd: 해제할 DC를 가진 윈도우의 핸들
    - hDC: 해제할 DC핸들

(2). 실행화면

1과 달리 창의 변화가 와도 유지됨.

 

 

3. 메뉴바(선 색깔 변경) 를 만들어 보자.

(0) 구현사항

메뉴바를 통해 선색깔(빨/녹/파)을 선택할 수 있게 만들 것이다.

default색상을 빨강으로 설정할 것이다.

line구조체는 COLORREF penColor를 가질 것이고, 이를 통해 색상을 구현할 것이다.


(1)-1.  리소스 다루기

- 프로젝트 > 새 항목 추가 > 좌측 리소스(.rc)추가 > resoure.rc 탭 확인하기
- Resource.rc 우클릭 > menu 새로 만들기 > 항목 이름 설정하기
- 우측하단 메뉴 편집기 > ID패널 확인 (편한대로 바꾸기, RED, GREEN, BLUE)

- 솔루션 > 헤더파일 > resource.h에 ID를 다시 제대로 확인.

 

(1)-1. cpp 소스코드

#include "resource.h"  // 리소스 적용하기 위한 헤더파일 적용

/* WinMain 함수에서 MenuID 적용하기 */
	//WndClass.lpszMenuName = NULL;
	WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    
/* 구조체에 색상 속성 추가하기 */
typedef struct _line {
	POINT p[1000];
	int iPointCount;
	COLORREF penColor; // 색상 속성
} line;  //선 객체 한 개의 표현

/* WndProc에 사용할 전역변수 설정 */
line lines[500];
int iLineCount; // 선 객체의 총 수
COLORREF CurrentPenColor = RGB(255, 0, 0); // 현재 선 객체의 색상
// int iTempPointCount; // 한 개의 선 객체 내의 점개수 카운트
/* WndProc내의 지역변수 */
	HDC hdc;
	PAINTSTRUCT ps; //그려낼 화면
	static int x;
	static int y;
	HPEN hPen; // 색상조절을 위한 펜 객체
	//static BOOL bNowDraw=FALSE;
 
 
 /* WndProc내의 Switch > WMCOMMAND */
 	case WM_COMMAND: //메뉴바 적용하기
		switch (LOWORD(wParam)) {
		case IDM_RED:
			CurrentPenColor = RGB(255, 0, 0);
			break;
		case IDM_GREEN:
			CurrentPenColor = RGB(0, 255, 0);
			break;
		case IDM_BLUE:
			CurrentPenColor = RGB(0, 0, 255);
			break;
		}
		return 0;
        
/* WndProc내의 Switch > WM_LBUTTONDOWN */
	case WM_LBUTTONDOWN: // 선 그리기 시작
		x = LOWORD(lParam);
		y = HIWORD(lParam);
		//	bNowDraw=TRUE;

		/* 구조체에 선 정보 (시작점) 저장하기 */
		lines[iLineCount].p[lines[iLineCount].iPointCount].x = x;
		lines[iLineCount].p[lines[iLineCount].iPointCount].y = y;
		lines[iLineCount].iPointCount++;
		lines[iLineCount].penColor = CurrentPenColor;
		//lines[iLineCount].p[iTempPointCount].x = x;
		//lines[iLineCount].p[iTempPointCount].y = y;
		//iTempPointCount++;
		return 0;
        
 /* WndProc내의 Switch > WM_MOUSEMOVE */
 	case WM_MOUSEMOVE:
 	if (wParam & MK_LBUTTON) {  // & = 비트연산자  // C언어에서 0 = False, 0이외의 수 = True
			// MK_LBUTTON   0x0001 ->00000....0001
			// MK_RBUTTON   0x0002 ->00000....0010 

			hdc = GetDC(hWnd);

			/* 펜 설정 */
			hPen = CreatePen(PS_SOLID, 1, lines[iLineCount].penColor);
			SelectObject(hdc, hPen);

			MoveToEx(hdc, x, y, NULL);
			x = LOWORD(lParam);
			y = HIWORD(lParam);
			LineTo(hdc, x, y);
			ReleaseDC(hWnd, hdc);
			
			/* 선 객체 정보 저장 (움직이는 점) */
			lines[iLineCount].p[lines[iLineCount].iPointCount].x = x;
			lines[iLineCount].p[lines[iLineCount].iPointCount].y = y;
			lines[iLineCount].iPointCount++;
			//lines[iLineCount].p[iTempPointCount].x = x;
			//lines[iLineCount].p[iTempPointCount].y = y;
			//iTempPointCount++;
		}
		return 0;
        
 /* WndProc내의 Switch > WM_LBUTTONUP */
 	case WM_LBUTTONUP:
 		//	bNowDraw=FALSE;
		/* 선 객체의 정보 저장 ( 끝 점) */
		//lines[iLineCount].iPointCount = iTempPointCount;
		iLineCount++; // 선 하나 증가
		//iTempPointCount = 0;  // 초기화
		return 0;
 
  /* WndProc내의 Switch > WM_PAINT */
  	case WM_PAINT: // 저장된 선의 정보 그리기 실행
		hdc = BeginPaint(hWnd, &ps);
		for (int i = 0; i < iLineCount; i++) {
			hPen = CreatePen(PS_SOLID, 1, lines[i].penColor);
			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);
			}
		}
		EndPaint(hWnd, &ps);
		return 0;​

 

  • SelectObject( HDC hdc,HGDIOBJ hgdiobj);
    그리기에 사용할 GDI 오브젝트를 변경하고자 할 때 이 오브젝트를 만든 후 이 함수로 DC에 선택해 주어야 한다. 
    - hgdiobj: DC에 선택하고자 하는 GDI 오브젝트의 핸들, 이 obj는 CreatePen, CreateSolidBrush, CreateFont 등의 함수로 생성한 obj이거나 GetStrockObject로 구한 스톡 오브젝트여야 한다.

 

(2) 실행화면