윈도우 프로그래밍/MFC_프로그래밍

윈도우 프로그래밍 (MFC 리소스 윈도우 메뉴 만들기 개념 및 예제)

피노키오이 2021. 4. 14. 01:32
반응형

이번에는 MFC 프로그래밍 중 윈도 메뉴에 대해서 알아보도록 하겠습니다.

.

.

.

MFC 프로그래밍에서 윈도 윈도 메뉴를 만들기 위해서는 리소스 파일을 만들고 윈도에 메뉴를 등록할 수 있습니다.

따라서 윈도 메뉴를 만들기 위해 우선 리소스를 추가해야 합니다.

.

.

.

그렇다면 우선 리소스에 대해서 간단히 알아보도록 하겠습니다.

리소스란?

.

.

리소스는 화면을 통해 사용자에게 입력받고 출력하는 코드 외의 부분으로 커서, 메뉴, 아이콘, 문자열, 비트맵, 다이얼로그, 엑셀레이터 등등이 있습니다.

또한 리소스는 중복되지 않는 UINT 값의 아이디로 다룰 수 있습니다.

.

.

리소스 추가

[리소스 파일] 폴더에서 오른쪽 버튼을 눌러서 [리소스] -> [추가] 

.

.

[리소스 추가] 창에서 [Menu]를 선택한 후 새로 만들기를 누릅니다.

리소스 추가

 

.

.

.

리소스를 추가하게 되면

[리소스 파일] 폴더에. rc 파일로 리소스 정의 파일이 생성이 됩니다.

[헤더 파일] 폴더에는 resource.h 파일로 리소스 ID를 정의할 수 있는 파일이 생성이 됩니다.

.

.

.

메뉴 화면을 만들 수 있는데 

메뉴 ID와 여러 속성을 정의할 수 있습니다.

여기서 Popup 속성이 true이면 부메뉴가 만들어지고 false 면 부메뉴가 만들어지지 않습니다.

.

.

.

저는 각각의 설정을 아래와 같이 주었습니다.

메뉴 설정

.

.

각각의 ID 값으로는

Caption ID 속성
새글 ID_FILENEW False
열기 ID_FILEOPEN False
저장 ID_FILESAVE False
끝내기 ID_EXIT False
취소하기 ID_EDITUNDO False
복사하기 ID_EDITCOPY False
붙여넣기 ID_EDITPASTE False
프로그램 정보 ID_INFORM False

.

.

.

이렇게 만들어 놓은 메뉴는 이벤트 발생을 하게 되는데

그때 사용하는 것은 WM_COMMAND입니다.

WM_COMMAND는 메뉴 항목을 선택하면 WM_COMMAND가 발생이 되고 이것을 커맨드 메시지라 합니다.

커맨드 메시지는 iMgs 변수에 저장되어 WndProc() 함수에 전달합니다.

또한 어떤 메뉴 항목을 선택했는지는 wParam에 저장이 됩니다.

.

.

.

이제 코드에서 만들어 놓은 윈도 메뉴를 쓸려면 아래와 같이 해주어야 합니다.

#include "resource.h"	// 리소스 파일 첨부

wndclass.IpszMenuName = MAKEINTRESOURCE(IDR_MENU4_1); // 메뉴등록 

.

.

여기서 IDR_MENU4_1는 아까 만들어 놓은 메뉴의 이름입니다.

.

.

.

MessageBox

메시지 박스 함수

int MessageBox(HWND hwnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

여기서 uType에 올 수 있는 상수와 변환 상수

MB_OK : IDOK

MB_OKCANCEL : IDOK, IDCANCEL

MB_YESNO : IDYES, IDNO

MB_YESNOCANCEL : IDYES, IDNO, IDCANCEL

.

.

.

MessageBox 예)

#include <windows.h>
#include <TCHAR.H>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpszCmdLine, int nCmdShow)
	// UNICODE 사용시 wWinMain() 형태 	
	// hPrevInstance 이전 인스턴스 항상 0값
	// lpszCmdLine > 외부에서 (내부로) 입력받는 변수
	// nCmdShow 윈도우 출력 형태에 관련한 값
{
	HWND     hwnd;
	MSG		 msg;
	WNDCLASS WndClass;
	WndClass.style = CS_HREDRAW | CS_VREDRAW;	//height, vertical redraw
	WndClass.lpfnWndProc = WndProc;		// Proc 설정
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hInstance = hInstance;
	WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// 형변환
	WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU4_1);
	WndClass.lpszClassName = _T("Window Class Name");
	RegisterClass(&WndClass);		// WndClass 등록
	hwnd = CreateWindow(_T("Window Class Name"),
		_T("Jung-Story"),		//
		WS_OVERLAPPEDWINDOW,		// 윈도우 스타일
		600, 400,					// 창출력좌표 x, y 
		600, 400,					// 창크기 x, y축
		NULL,						// 부모 윈도우
		NULL,						// 메뉴바 핸들
		hInstance,					// 인스턴스
		NULL						// 여분, NULL
		);
	ShowWindow(hwnd, nCmdShow);		// 윈도우 출력, WM_PAINT 출력내용 가져옴
	UpdateWindow(hwnd);				// WM_PAINT 출력내용 발생해서 출력하도록
									// hwnd 핸들을 통해 보여주고 갱신

	//ShowWindow(hwnd, SW_SHOW);	// 위와 같음
	//UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))	// 메시지 큐의 메시지를 가져옴
	{
		TranslateMessage(&msg);		// 키입력에 반응하는 메시지 변환, WM_KEYDOWN (키가 눌릴때) WM_CHAR 메시지 발생
		DispatchMessage(&msg);		// WndProc() 함수 호출과 WndProc()으로 메세지 전달
	}								// 종료는 WM_QUIT 발생할때 FALSE 리턴하면서 종료
	return (int)msg.wParam;			// wParam, lParam 윈도우 크기가 어떻게 변했는지, 변경된 클라이언트, 키보드, 마우스 값

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam)
	//	WinDef.h 에서 정의
	//	wPram > unsigned ptr, lParam > long ptr 
{	
	int answer;

	switch (iMsg)
	{
	case WM_CREATE:
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ID_FILENEW:
			MessageBox(hwnd, _T("새파일을 열겠습니까?"), _T("새파일 선택"), MB_OKCANCEL);
			break;
		case ID_EXIT:
			answer = MessageBox(hwnd, _T("파일을 저장하고 끝내겠습니까"), _T("끝내기 선택"), MB_YESNOCANCEL);
			if (answer == IDYES || answer == IDNO)
				PostQuitMessage(0);
			break;
		case ID_EDITUNDO:
			MessageBox(hwnd, _T("편집을 취소 하겠습니까?"), _T("편집 취소"), MB_OKCANCEL);
		default:
			break;
		}
	
	
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

.

.

.

[파일] -> [새글]

.

.

[파일] -> [끝내기]

.

.

[편집] -> [취소하기]

.

.

.

이제 파일 열기를 했을 경우 처리 방법에 대해서 알아보도록 하겠습니다.

.

.

.

파일 열기 상자 처리 절차

OPENFILENAME 구조체 할당

열기 함수 호출 -> 파일 이름 획득

.

.

.

필터 지정 방법

필터의 용도

1. 표시되는 파일 이름을 걸러 줍니다.

2. 정의 시 공문자 삽입 안 하도록 해줍니다.

사용 예) TCHAR filter [] = _T("텍스트 파일(*. txt) \0 *. txt \0 모든 파일(*.*) \0 *.* \0");

.

.

.

파일 열기 예)

#include <windows.h>
#include <TCHAR.H>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpszCmdLine, int nCmdShow)
	// UNICODE 사용시 wWinMain() 형태 	
	// hPrevInstance 이전 인스턴스 항상 0값
	// lpszCmdLine > 외부에서 (내부로) 입력받는 변수
	// nCmdShow 윈도우 출력 형태에 관련한 값
{
	HWND     hwnd;
	MSG		 msg;
	WNDCLASS WndClass;
	WndClass.style = CS_HREDRAW | CS_VREDRAW;	//height, vertical redraw
	WndClass.lpfnWndProc = WndProc;		// Proc 설정
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hInstance = hInstance;
	WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// 형변환
	WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU4_1);
	WndClass.lpszClassName = _T("Window Class Name");
	RegisterClass(&WndClass);		// WndClass 등록
	hwnd = CreateWindow(_T("Window Class Name"),
		_T("Jung-story"),		//
		WS_OVERLAPPEDWINDOW,		// 윈도우 스타일
		600, 400,					// 창출력좌표 x, y 
		600, 400,					// 창크기 x, y축
		NULL,						// 부모 윈도우
		NULL,						// 메뉴바 핸들
		hInstance,					// 인스턴스
		NULL						// 여분, NULL
		);
	ShowWindow(hwnd, nCmdShow);		// 윈도우 출력, WM_PAINT 출력내용 가져옴
	UpdateWindow(hwnd);				// WM_PAINT 출력내용 발생해서 출력하도록
									// hwnd 핸들을 통해 보여주고 갱신

	//ShowWindow(hwnd, SW_SHOW);	// 위와 같음
	//UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))	// 메시지 큐의 메시지를 가져옴
	{
		TranslateMessage(&msg);		// 키입력에 반응하는 메시지 변환, WM_KEYDOWN (키가 눌릴때) WM_CHAR 메시지 발생
		DispatchMessage(&msg);		// WndProc() 함수 호출과 WndProc()으로 메세지 전달
	}								// 종료는 WM_QUIT 발생할때 FALSE 리턴하면서 종료
	return (int)msg.wParam;			// wParam, lParam 윈도우 크기가 어떻게 변했는지, 변경된 클라이언트, 키보드, 마우스 값

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam)
	//	WinDef.h 에서 정의
	//	wPram > unsigned ptr, lParam > long ptr 
{
	OPENFILENAME OFN;
	TCHAR str[100], lpstrFile[100] = _T("");
	TCHAR filter[] = _T("C++ 소스와 헤더 파일 \0 *.cpp \0 Every File(*.*) \0 *.* \0Text File\0*.txt,*.doc\0");

	switch (iMsg)
	{
	case WM_CREATE:
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
				case ID_FILEOPEN:
					memset(&OFN, 0, sizeof(OPENFILENAME));
					OFN.lStructSize = sizeof(OPENFILENAME);
					OFN.hwndOwner = hwnd;
					OFN.lpstrFilter = filter;
					OFN.lpstrFile = lpstrFile;
					OFN.nMaxFile = 100;
					OFN.lpstrInitialDir = _T(".");
					if (GetOpenFileName(&OFN) != 0) {
						_stprintf_s(str, _T("%s 파일을 열겠습니까?"), OFN.lpstrFile);
						MessageBox(hwnd, str, _T("열기 선택"), MB_OK);
					}
					break;
			break;
		default:
			break;
		}


	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

.

.

.

[파일] -> [열기] 

.

.

.

이번에는 원을 선택하고 붙여 넣기 하는 방법에 대해서 예제를 보도록 하겠습니다.

#include <windows.h>
#include <TCHAR.H>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpszCmdLine, int nCmdShow)
	// UNICODE 사용시 wWinMain() 형태 	
	// hPrevInstance 이전 인스턴스 항상 0값
	// lpszCmdLine > 외부에서 (내부로) 입력받는 변수
	// nCmdShow 윈도우 출력 형태에 관련한 값
{
	HWND     hwnd;
	MSG		 msg;
	WNDCLASS WndClass;
	WndClass.style = CS_HREDRAW | CS_VREDRAW;	//height, vertical redraw
	WndClass.lpfnWndProc = WndProc;		// Proc 설정
	WndClass.cbClsExtra = 0;
	WndClass.cbWndExtra = 0;
	WndClass.hInstance = hInstance;
	WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// 형변환
	WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU4_1);
	WndClass.lpszClassName = _T("Window Class Name");
	RegisterClass(&WndClass);		// WndClass 등록
	hwnd = CreateWindow(_T("Window Class Name"),
		_T("Jung-Story"),		//
		WS_OVERLAPPEDWINDOW,		// 윈도우 스타일
		600, 400,					// 창출력좌표 x, y 
		600, 400,					// 창크기 x, y축
		NULL,						// 부모 윈도우
		NULL,						// 메뉴바 핸들
		hInstance,					// 인스턴스
		NULL						// 여분, NULL
		);
	ShowWindow(hwnd, nCmdShow);		// 윈도우 출력, WM_PAINT 출력내용 가져옴
	UpdateWindow(hwnd);				// WM_PAINT 출력내용 발생해서 출력하도록
									// hwnd 핸들을 통해 보여주고 갱신

	//ShowWindow(hwnd, SW_SHOW);	// 위와 같음
	//UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))	// 메시지 큐의 메시지를 가져옴
	{
		TranslateMessage(&msg);		// 키입력에 반응하는 메시지 변환, WM_KEYDOWN (키가 눌릴때) WM_CHAR 메시지 발생
		DispatchMessage(&msg);		// WndProc() 함수 호출과 WndProc()으로 메세지 전달
	}								// 종료는 WM_QUIT 발생할때 FALSE 리턴하면서 종료
	return (int)msg.wParam;			// wParam, lParam 윈도우 크기가 어떻게 변했는지, 변경된 클라이언트, 키보드, 마우스 값

}

#include <math.h>
#define BSIZE 20
int x[10] ,y[10], count;
float LengthPts(int x1, int y1, int x2, int y2)
{
	return(sqrt((float)((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))));
}
BOOL InCircle(int x, int y, int mx, int my)
{
	if (LengthPts(x, y, mx, my) < BSIZE) return TRUE;
	else return FALSE;
}
int SelectCircle(int mx, int my)
{
	int i;
	for (i = 0; i < count; i++)
		if (InCircle(x[i], y[i], mx, my))
			return i;
	return -1;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
	WPARAM wParam, LPARAM lParam)
	//	WinDef.h 에서 정의
	//	wPram > unsigned ptr, lParam > long ptr 
{
	HDC	hdc;
	PAINTSTRUCT ps;
	static HMENU hMenu, hsubMenu;
	int mx, my, i, tmp;
	static int Select;	// 현재 선택한 원 위치
	static BOOL Copy;	// COPY 상태일때만 PASTE 활성화 -> PASTE 상태 변수 필요 없음

	switch (iMsg)
	{
	case WM_CREATE:
		hMenu = GetMenu(hwnd);
		hsubMenu = GetSubMenu(hMenu, 1);
		Select = -1;	// 선택한 원 없음
		Copy = FALSE;	// 선택한 원 없음
		x[0] = 20;	y[0] = 20;	// 첫번째 원 위치
		count = 1;	// 생성된 원 개수
		break;
	case WM_PAINT:
		EnableMenuItem(hsubMenu, ID_EDITCOPY,(Select > -1)? MF_ENABLED : MF_GRAYED);
		EnableMenuItem(hsubMenu, ID_EDITPASTE, Copy ? MF_ENABLED : MF_GRAYED);
		
		hdc = BeginPaint(hwnd, &ps);
		for (i = 0; i < count; i++) {
			if (i != Select) {	// 그 외의 원
				Ellipse(hdc, x[i] - 20, y[i] - 20, x[i]+20, y[i]+20);
			}
			else {	// 선택한 원
				SelectObject(hdc, CreatePen(PS_SOLID, 2, RGB(255, 0, 0)));
				Ellipse(hdc, x[i] - 20, y[i] - 20, x[i]+20, y[i]+20);
				SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(0, 0, 0)));
			}
		}
		EndPaint(hwnd, &ps);
		return 0;
	case WM_LBUTTONDOWN:
		mx = LOWORD(lParam);
		my = HIWORD(lParam);
		tmp = 0;
		
		// 선택한 원 가져오기
		tmp = SelectCircle(mx, my);
		if (Select != tmp) {	// 새로운 원 선택
			Copy = FALSE;
			Select = tmp;
		}
		InvalidateRgn(hwnd, NULL, TRUE);
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ID_EDITCOPY:
			Copy = TRUE;
			break;
		case ID_EDITPASTE:
			if (count < 10) {
				x[count] = x[count - 1] + 40;
				y[count] = y[count - 1] + 40;
				count++;
			}
			break;
		}
		InvalidateRgn(hwnd, NULL, TRUE);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

.

.

.

 

.

.

.

이렇게 이번에는 MFC_프로그래밍 리소스에 대해서 알아보았습니다.

 

반응형