윈도우 프로그래밍 (MFC 리소스 윈도우 메뉴 만들기 개념 및 예제)
이번에는 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_프로그래밍 리소스에 대해서 알아보았습니다.