프로젝트/MFC 프로그래밍

4. MFC Programming : 쉽게 배우는 MFC 윈도우 프로그래밍(CH 6)

잡학다식을꿈꾼다 2023. 1. 16. 15:11
반응형

 이 글은 공부하면서 배운 것들을 간략하게 작성한 것이다. 그만큼 두서도 없고, 기재되어 있는 코드도 어느정도 MFC 구조에 대한 이해가 없으면 파악하기 어려울 수 있다. 이 점을 참고하기를 바란다.

 메뉴란 응용 프로그램에서 선택할 수 있는 명령의 집합으로, 계층 구조로 이루어진 인터페이스 요소이다. 메뉴에는 여러가지 종류의 메뉴가 있다. 다음은 윈도우가 지원하는 메뉴 종류이다.

 

  • Top-Level Menu : 타이틀 바 아래쪽에 위치함, 일반적으로 생각하는 메뉴
  • Drop-Down Menu : 최상위 메뉴를 선택하면 펼처지는 메뉴
  • Context Menu : 마우스 오른쪽 버튼을 누르면 나오는 메뉴, 마우스 커서의 위치에 따라서 뜨는 내용이 달라질 수 있다.
  • Pop-Up Menu :  사용자의 행위 결에 따라서 튀어나오는 메뉴(Context Menu와 Drop-Down Menu가 이에 해당한다.)
  • System Menu : 타이틀 바 왼쪽에 있는 아이콘을 클릭하면 펼쳐지는 메뉴

 

 다음은 실습의 내용이다. MFC를 이용하여 메뉴, 툴바, 상태바의 형태와 동작을 살펴보는 것을 목적으로 한다. 메뉴를 생성하는 방법은 크게 두 가지가 있다. 하나는 매뉴 리소스를 정의하여 실행 파일에 포함시킨 뒤 프로그램 실행 중에 로드하여 사용하는 것이고, 다른 하나는 코드를 실행하여 메뉴를 생성한 후 윈도우에 붙여서 사용하는 것이다. 일반적으로 사용되는 방법은 첫 번째 것이다. 

 메뉴를 일일이 코드로 구현할 필요는 없다. Visual Studio는 사용자가 메뉴를 그리고, 그 메뉴를 리소스로 등록하여 아주 약간의 수정만으로 정의한 리소스를 사용할 수 있도록 기능을 제공한다. 메뉴를 그리는 방법에 대해 간단하게만 언급하자면, 리소스 폴더를 우클릭하면, 리소스 추가 항목이 있고, 메뉴를 추가하는 항목이 있을 것이다. 선택해서 원하는 메뉴를 만들면 된다. 그 후에는 C{프로젝트 생성시 쓴 name}App.cpp 파일의 InitInstance()를 약간 수정하면 된다. 코드는 다음과 같다. 

 

BOOL C{App-Name}App::InitInstance(){
	... 생략 ...
    
    CFrameWnd* pFrame = new CMainFrame;
	if (!pFrame)
		return FALSE;
	m_pMainWnd = pFrame;
	// create and load the frame with its resources
	pFrame->LoadFrame(IDR_MENU							//여기에 등록된 메뉴 아이디를 넣는다
    	,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, nullptr,
		nullptr);
    
    ... 생략 ...
}

 

 리소스 파일을 이용하는 것 외에도 바로  메인 메뉴를 코드로 만들어 사용하는 방법도 있다. 다음의 코드를 CMainFrame::OnCreate()에 추가해 주면 된다.

 

CMenu menuMain;
	menuMain.CreateMenu();

	//메뉴를 그리는 과정(계층도를 그리는 것과 유사하다.)
    CMenu menuPopup;
	menuPopup.CreatePopupMenu();

	menuPopup.AppendMenu(MF_STRING, 201, _T("RED(&R)"));
	menuPopup.AppendMenu(MF_STRING, 202, _T("BLUE(&B)"));
	menuPopup.AppendMenu(MF_STRING, 203, _T("GREEN(&G)"));

	menuMain.AppendMenu(MF_POPUP, (UINT)menuPopup.Detach(), _T("COLOR(&C)"));
	
    //그린 메뉴를 윈도우에 붙인다.
	SetMenu(&menuMain);
    //메뉴 객체와 메뉴를 분리한다.
	menuMain.Detach();

 

 메뉴를 만들어 보았으니 이제는 만든 메뉴에서 항목을 선택할 때, 특정 명령이 나올 수 있도록 해보자. 메뉴에서 특정 항목을 선택할 때마다 윈도우는 WM_COMMAND 메시지를 송출한다. 그러나 WM_COMMAND 메시지는 메뉴 항목을 선택할 때뿐만 아니라 컨트롤에서도 발생하므로 항목별로 처리 함수를 작성해줄 필요가 있다. Visual Studio는 처리 함수를 쉽게 작성할 수 있도록 기능들을 제공한다. 다음은 예제이다.

 

void CChildView::OnPaint() 
{
	//m_color은 CChildView 클래스에 정의되어 있다.
    
    CPaintDC dc(this); // device context for painting
	
	CFont font;
	font.CreatePointFont(300, _T("궁서"));
	dc.SelectObject(&font);
	dc.SetTextColor(m_color);

	CRect rect;
	GetClientRect(&rect);
	CString str = _T("Menu Test");
	dc.DrawText(str, &rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}



void CChildView::OnRedLighter()
{
	m_color = RGB(255,0,0);
	Invalidate();
}

 

 메뉴 항목 갱신하기. 때로는 선택에 따라서 메뉴 항목에 변경 상항이 있는 경우도 있다. 그럴 때는 OnUpdate*(CCmdUI*) 함수를 이용하여 메뉴의 객체를 바꾸면 된다. 

 

void CChildView::OnUpdateRedLighter(CCmdUI* pCmdUI)
{
	//메뉴 관련 변경 내용을 넣으면 됩니다.
    //pCmdUI->Enable() : 활성화 상태 변경
    //pCmdUI->SetCheck() : 체크 상태 변경
    //pCmdUI->SetRadio() : 라디오 표시 상태 변경
    //pCmdUI->SetText() : 문자열 변경
}

 

 컨텍스트 메뉴는 마우스 오른쪽 버튼을 클릭할 때 열린다. 아우스 커서의 위치 또는 현재 작업하는 내용에 따라 메뉴 항목을 표시한다는 특징이 있다. 윈도우 운영체제는 컨텍스트 메뉴를 지원하기 위해 WM_CONTEXTMENU 메시지를 제공한다. 사용법은  기존의 이벤트 함수를 추가하는 것과 같다. 다음은 예제 코드이다.

 

void CChildView::OnContextMenu(CWnd* pWnd, CPoint point)
{
	CMenu menu;
	menu.LoadMenu(IDR_MAINFRAME);
	CMenu* pMenu = menu.GetSubMenu(0);
	pMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, pWnd, 0);
	//TrackMenu(UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect)
}

 

 시스템 메뉴를 사용하는 방법은 다음과 같다. 

 

//추가하거나 수정해야 할 코드
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){
	...생략...
    
    CMenu* pSysMenu = GetSystemMenu(FALSE);
    //닫기 비활성화
    pSysMenu->EnableMenuItem(SC_CLOSE, MF_GRAYED);
    //부리자 항목을 추가한다
    pSysMenu->AppendMenu(MF_SEPARATOR);
    //ID가 16인 항목을 추가한다
    pSysMenu->AppendMenu(MF_STRING, 16, _T("이 프로그램은 ..."));
    
    return 0;
}

void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
	if((nID & 0xFFF0) == 16){
		MessageBox(_T("시스템 메뉴를 테스트합니다."), _T("이 프로그램은..."));
		return;
	}

	CFrameWnd::OnSysCommand(nID, lParam);
}

 

 윈도우는 가속키를 지원한다. 가속키의 예시로 대표적인 것은 Cntrl + C, Cntrl + V 같은 것이 있다. Visual Studio는 쉽게 가속키를 설정할 수 있도록 기능을 지원한다.

  툴 바는 일반적으로 메뉴 항목의 기능을 빠르게 수행하는 목적으로 사용되며, 때로는 메뉴 항목에 없는 기능을 제공한다.

종류로는 CControlBar, CToolBar로 두 개의 클래스로 기능을 나누어 제공한다. 툴 바 리소스는 MFC 앱을 초기에 생성할 때 제공된다.(당연하지만 편집도 가능하다.) 다음은 메인 프레임 클래스에서 툴 바를 정의한 부분이다.

 

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){
	...생략...
    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}
    ...생략...
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);
}

 

 툴 바의 각 요소에 처리 함수를 추가하는 것은 앞서 처리함수를 처리하는 방법과 같다.

상태바는 윈도우의 맨 아래에 위치해 있어 프로그램의 현재 상태를 보여준다. CControllBar, CStatusBar로 나뉘어져 기능을 제공한다. 메인 프레임에 스테이터스 바는 다음과 같이 정의되어 있다.(당연히 편집해서 사용할 수 있다.)

 

int CMainFrame::OnCreate(LPREATESTRUCT lpCreateStruct){
	...생략...
    if (!m_wndStatusBar.Create(this))
	{
		TRACE0("Failed to create status bar\n");
		return -1;      // fail to create
	}
	m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
    ...생략...
}

 

반응형