[windowAPI] 제4장 윈도우 각종 메뉴 구성
핵심 API로 배우는 윈도우 프로그래밍 4장을 공부하고 정리한 내용입니다.
일반적인 프로그램의 윈도우 상단에서 볼 수 있는 메뉴창이다
그것을 구성하는 방법을 배운다
Visual Studio를 사용하면 어렵지 않게 템플릿을 생성 할 수 있다.
이제까지 만져본적 없는 리소스 파일을 생성해준다.
그러면 리소스 파일에 rc 파일과 헤더파일에 h 파일 하나가 생성된다.
리소스 파일을 클릭하면 메뉴 구성을 시작적으로 볼 수 있고
헤더파일을 클릭하면 그것이 코드로 나타나있다.
리소스 파일을 이용에 매우 직관적이고 쉽게 메뉴를 구성 할 수 있다.
메뉴를 입력하고 클릭하면 속성 탭에 메뉴 편집기가 나타나게 된다.
메뉴 버튼을 누르거나 여러가지 기능을 구현하려면 어떤 메뉴를 눌렀는지 프로그램에 전달해줘야 하므로
메뉴에 ID를 입력해주어야 한다. 처음 생성하면 랜덤숫자가 지정되어 있지만 코드를 작성할때 가독성이 매우 떨어지므로
메뉴에 해당하는 기능을 이용해 적당히 이름을 지어주는게 좋다.
메뉴에 이름을 넣고 ID도 지어줬으면 헤더파일은 어떻게 구성되어 있을까?
보라색 글씨가 내가 만든 메뉴의 ID 이다. 숫자로 되어있는 것은 아직 ID를 직접 만들어 주지 않아 임의의 숫자가 할당되어 있고 나머지는 메뉴의 기능에 맞게 ID를 재설정 해 주었다.
이제 메인 코드에 리소스 파일을 적용해 메뉴를 구성해 준다.
#include "resource.h"
WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU4_1); //메뉴 이름
먼저 작성한 메뉴를 불러와야 하기 때문에 include 해준다.
또한 WinMain 함수의 WindClass 구조체에 값을 하나 추가 해준다.
먼저 새글 탭을 누르면 "새 파일을 열겠습니까?" 라는 메시지 박스를 보여주고
끝내기 탭을 누르면 "파일을 저장하고 끝내겠습니까?" 라는 메시지 박스를 보여주는 것을 구현한다.
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
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;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
메뉴 항목 선택시 WM_COMMAND 메시지가 발생하므로 해당 메시지를 처리해준다.
새글 메뉴에는 ID_FILENEW , 끝내기 메뉴에는 ID_EXIT 아이디를 지정해 주었다.
각각의 메뉴를 클릭하면 해당 ID가 전달되어 기능을 수행한다.
MessageBox() 함수를 이용하면 팝업 메시지를 띄울 수 있다.
공용대화상자를 만들어보자
여러가지 작업을 할 수 있는 이러한 창을 공용대화상자라고 한다.
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
OPENFILENAME OFN;
TCHAR str[100], lpstrFile[100] = _T("");
TCHAR filter[] = _T("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);
OutFromFile(OFN.lpstrFile, hwnd);
}
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
열기 메뉴에 ID_FILEOPEN 아이디를 지정해주고 메뉴를 클릭하면 어떤 파일을 열지 파일탐색기 공용대화상자를 연다.
filter를 이용하면 어떤 형식의 파일을 열지 지정해줄 수 있다.
void OutFromFile(TCHAR filename[], HWND hwnd)
{
FILE* fPtr;
HDC hdc;
int line;
TCHAR buffer[500];
line = 0;
hdc = GetDC(hwnd);
#ifdef _UNICODE
_tfopen_s(&fPtr, filename, _T("r, ccs = UTF-8"));
#else
_tfopen_s(&fPtr, filename, _T("r"));
#endif
while (_fgetts(buffer, 100, fPtr) != NULL)
{
if (buffer[_tcslen(buffer) - 1] == _T('\n'))
buffer[_tcslen(buffer) - 1] = NULL;
TextOut(hdc, 0, line * 20, buffer, _tcslen(buffer));
line++;
}
fclose(fPtr);
ReleaseDC(hwnd, hdc);
}
선택한 텍스트 파일에서 문자를 읽어오기 위한 OutFromFile() 함수를 작성한다.
텍스트 파일의 인코딩 형식이 여러가지일 수 있으므로 #ifdef로 분기시켜 처리해준다.
(요즘은 보통 유니코드일 것이다. 그러므로 UTF-8 을 적어준다.)
텍스트 파일의 내용을 한줄씩 불러와 화면에 출력한다.
열기 를 선택하면 팝업창을 한번 띄우고
텍스트 파일의 내용을 정상적으로 읽어들여 출력한다. (뒤에 잡동사니는 앞으로 포스팅할 예제를 같이 출력한 것이다..)
OPENFILENAME SFN;
case ID_FILESAVE:
memset(&SFN, 0, sizeof(OPENFILENAME));
SFN.lStructSize = sizeof(OPENFILENAME);
SFN.hwndOwner = hwnd;
SFN.lpstrFilter = filter;
SFN.lpstrFile = lpstrFile;
SFN.nMaxFile = 256;
SFN.lpstrInitialDir = _T(".");
if (GetSaveFileName(&SFN) != 0)
{
_stprintf_s(str, _T("%s 파일로 저장하겠습니까?"), SFN.lpstrFile);
MessageBox(hwnd, str, _T("저장하기 선택"), MB_OK);
}
break;
다른이름으로 저장 창을 띄우는 코드이다.
아까 메뉴에 있던 저장 메뉴에 ID_FILESAVE 아이디를 지정한후 switch 문에 작성해준다.
단 SFN 변수를 추가해주어야 한다.
저장 메뉴를 클릭하면
공용대화상자를 띄워주고
파일명을 입력해 저장을 누르면 팝업창을 띄워준다. 물론 진짜로 파일이 생성되지는 않는다
시험기간이라 좀처럼 시간이 나질 않는다...
하필이면 포스팅하려한날 카카오톡 서버가 터져 티스토리가 먹통이 되었기에...
그동한 공부했던 챕터4의 내용을 한번에 포스팅 한다.
계속