Overview 사용자인터페이스 메뉴명령을처리하고메뉴항목을적절하게갱신기법 컨텍스트메뉴와시스템메뉴를다루는방법 툴바를생성하고사용하는방법 상태바를생성하고사용하는방법 HCI Programming 2 (321190) 2007년가을학기 11/2/2007 박경신 2 메뉴용어 메뉴 프로그램에서선택할수있는명령집합 계층적요소로구성된사용자인터페이스 최상위메뉴 (top-level) = 메뉴바 (menu bar) 최상위메뉴 = 메뉴바 메뉴용어 메뉴항목 (menu item) 용어 의미 명령항목명령 (Command) 을수행하는메뉴항목. 선택하면 WM_COMMAND 메시지가발생한다. 팝업항목하위메뉴를화면에표시하는메뉴항목. 선택해도 WM_COMMAND 메시지가발생하지않는다. 명령항목 팝업항목 3 4
메뉴용어 하위메뉴 (submenu) 팝업항목을선택했을때화면에나타나는메뉴 드랍다운 (drop-down) 메뉴 최상위메뉴항목을클릭했을때펼쳐지는메뉴 사용자가특정항목을선택하거나취소하기전까지계속열린채로유지 메뉴용어 컨텍스트메뉴 (context menu) = 단축메뉴 마우스오른쪽버튼을누를때열리는메뉴 마우스커서의위치또는현재작업하고있는내용에따라서로다른메뉴항목이표시됨 Drop-down menu = 팝업메뉴 Context memu = 단축메뉴 = 팝업메뉴 메뉴용어 팝업메뉴 (pop-up menu) 사용자가무엇인가를선택했을때메뉴가튀어나온다 (Pop Up) 는뜻으로만든용어 드랍다운메뉴와컨텍스트메뉴가여기에속함 시스템메뉴 (system menu) = 윈도우메뉴 메뉴용어 액세스키 (access key) 와단축키 (shortcut key) 액세스키 - 메뉴가열린상태에서특정항목을키보드로빠르게선택 단축키 - 메뉴가열리지않은상태에서도키조합으로메뉴항목의기능을곧바로실행 System menu System menu = 윈도우메뉴 Access Key Shortcut Key Context memu 7 8
메뉴생성및추가방법 메뉴생성방법 방법 1 메뉴리소스를만들어메인프레임생성시로드하여사용 방법 2 프로그램코드에서메뉴클래스 (CMenu) 를사용하여메뉴를생성하고메뉴항목을구성하여추가 CWnd::SetMenu() 메인프레임윈도우에메뉴연결 동적생성메뉴를기존메뉴에추가 메뉴클래스 (CMenu) 를사용하여메뉴를생성하고메뉴의구성요소를설정 메인프레임의 CWnd::GetMenu() 또는메뉴리소스 ID를사용한 CMenu::LoadMenu() 를이용하여기존메뉴를얻기 9 기존메뉴에 CMenu::AppendMenu() 를이용하여생성된메뉴를추가 메뉴클래스 MFC 클래스 CMenu 메뉴를생성하여관리하는클래스 메뉴생성, 메뉴항목추가등다양한함수제공 CCmdUI CObject 의파생클래스가아닌독립된클래스 사용자인터페이스의요소상태를변경할수있는클래스 활성화상태변경, 체크상태변경, 문자열변경등메뉴와툴바및상태바에필요한기능지원 10 CMenu Class 메뉴 : 윈도우운영체제에서관리하는구조메뉴객체 : 프로그램에서사용되는 C++ 객체 메뉴생성 메뉴생성및항목추가등메뉴관리를위한클래스 주요메소드 CreateMenu() : 최상위메뉴생성후메뉴객체와연결 (attach) CreatePopupMenu() : 팝업메뉴생성후메뉴객체와연결 (attach) AppendMenu() : 새로운메뉴항목을메뉴에추가 InsertMenu() : 메뉴항목삽입 DeleteMenu() : 메뉴항목삭제 LoadMenu() : 리소스로존재하는메뉴의 ID를이용하여메뉴를얻기 GetSubMenu() : 메뉴의인덱스를이용하여하위메뉴추출 TrackPopupMenu() : WM_CONTEXTMENU 메시지발생시팝업메뉴를표시 Attach() : 메뉴와메뉴객체를연결 Detach() : 메뉴와메뉴객체를분리, 특정메소드내에서11 사용된메뉴객체는파괴되더라도메뉴구조는유지시켜야할때사용 AppWizard 가생성한코드 BOOL CUIApp::InitInstance() CMainFrame* pframe = new CMainFrame; m_pmainwnd = pframe; pframe->loadframe(idr_mainframe, WS_OVERLAPPEDWINDOW FWS_ADDTOTITLE, NULL, NULL); pframe->showwindow(sw_show); pframe->updatewindow(); return TRUE; 12
메뉴생성 방법 1: 메뉴리소스를작성하여메뉴생성 리소스뷰의메뉴에서 IDR_MAINFRAME 선택 메뉴생성 메뉴항목속성속성의미 1. Resource View 의 Menu 에서 IDR_MAINFRAME 선택 2. 현재프로젝트에설정되어있는메뉴를추가 / 편집 3. Menu Item의 Properties를열고Caption 등메뉴항목의속성추가 13 / 편집 ID Caption Separator Pop-up 내부적으로메뉴항목을구분하는번호이며일반적으로 ID_ 메뉴이름 _ 항목이름형태로만든다. 예 ) ID_EDIT_CUT 화면에표시되는문자열로액세스키를지정하려면해당문자앞에 '&' 기호를사용한다. 단축키를사용할경우 '\t' 기호를삽입하여단축키를나타내는문자열이탭위치에정렬되도록한다. 예 ) 잘라내기 (&T)\tCtrl+X 메뉴항목을구분하는가로줄이표시된다. 설정하면명령항목이아닌팝업항목이된다. 최상위메뉴는대개 Pop-up 속성을가진다. 14 메뉴생성 메뉴항목속성 메뉴생성 메뉴항목속성 속성 Inactive Break Checked 의미 메뉴항목이표시되지만사용하지는못한다. 일반적으로메뉴항목은하나의열 (Column) 에표시되지만항목의개수가많을경우두개이상의열에표시되게할수있다. None: 메뉴항목에대해열을분리하지않는다 Column: 다음메뉴항목에대해열을분리하여표시한다 Bar: 다음메뉴항목에대해열에분리하고분리선도표시한다메뉴항목의왼쪽에체크표시를한다. 속성 Grayed Help Prompt 의미 메뉴항목이흐리게표시되어현재사용할수없음을나타낸다. 윈도우의오른쪽끝위치에메뉴가표시되도록한다. 주로 Help 메뉴항목에이속성을설정한다. MFC 로작성한프로그램에서만사용할수있는속성으로, 툴바와상태바에표시될문자열을나타낸다. '\n' 을기준으로앞쪽문자열은상태바에표시되며뒤쪽문자열은툴팁에표시된다. 예 ) 선택부분을잘라내어클립보드에넣습니다 \n 잘라내기 15 16
메뉴생성 방법 2: 프로그램실행중전체메뉴생성하기 메뉴생성 방법 2: 프로그램실행중전체메뉴생성하기 int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) CMenu menumain; // 메뉴객체생성 menumain.createmenu(); // 메뉴바생성및메뉴객체에연결 CMenu menupopup; menupopup.createpopupmenu(); // 팝업메뉴객체생성 // 3개메뉴항목추가 menupopup.appendmenu(mf_string, 201, " 빨강 (&R)"); menupopup.appendmenu(mf_string, 202, " 초록 (&G)"); menupopup.appendmenu(mf_string, 203, " 파랑 (&B)"); // 메뉴바에팝업메뉴추가 menumain.appendmenu(mf_popup, (UINT_PTR)menuPopup.Detach(), " 색상 (&C)"); // 메뉴를윈도우메인프레임에연결 SetMenu(&menuMain); menumain.detach(); // 메뉴객체와메뉴를분리 return 0; 17 18 AppendMenu 함수 메뉴생성 메뉴항목을메뉴에추가하는함수 BOOL AppendMenu(UINT nflags, UINT_PTR nidnewitem=0, LPCTSTR lpsznewitem=null) nflags 의미 nidnewitem lpsznewitem MF_STRING 메뉴항목이문자열 새로운항목의 ID 메뉴항목의캡션문자열 MF_POPUP 메뉴항목이팝업메뉴를가짐 팝업메뉴의핸들 (HWND 형식 ) 메뉴항목의캡션문자열 MF_CHECKED MF_UNCHECKED MF_ENABLED MF_DIABLED MF_GRAYED 메뉴항목속성표시 MF_SEPARATOR Separator 속성설정 프로그램실행중추가메뉴생성하기 int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) CMenu Popup1; // Item2 의하위메뉴생성 Popup1.CreatePopupMenu(); Popup1.AppendMenu(MF_STRING, 301, "&1"); Popup1.AppendMenu(MF_STRING, 302, "&2"); Popup1.AppendMenu(MF_STRING, 303, "&3"); Popup1.AppendMenu(MF_STRING, 304, "&4"); CMenu Popup2; // 메뉴항목추가 Popup2.CreatePopupMenu(); Popup2.AppendMenu(MF_STRING MF_CHECKED, 201, "Item&1"); 19 20
메뉴생성 메뉴명령처리 프로그램실행중추가메뉴생성하기 Popup2.AppendMenu(MF_POPUP, (UINT_PTR)Popup1.Detach(), "Item&2"); Popup2.AppendMenu(MF_STRING, 203, "Item&3"); CMenu *ptoplevel = GetMenu(); // 연습메뉴를최상위메뉴에붙인다 ptoplevel->appendmenu(mf_popup, (UINT_PTR)Popup2.Detach(), " 연습 (&X)"); return 0; 메뉴명령처리과정 1. 명령항목을마우스나키보드로선택 2. WM_COMMAND 메시지발생 3. WM_COMMAND 메시지핸들러에서메뉴명령처리 MFC의메뉴명령처리방법 각각의메뉴항목에대해함수를따로작성 명령핸들러 (Command Handler) ON_COMMAND( 메뉴ID, 함수명 ) 매크로를이용하여메뉴항목과함수연결 명령라우팅 명령핸들러를작성하는위치에관계없이처리 21 22 Command Routing WM_COMMAND 메시지는 CCmdTarget클래스로부터상속된하위객체에서처리가능 WM_COMMAND 메시지를처리할메시지핸들러가여려객체에서정의되어있는경우명령전달경로의순서로해당메시지가처리 CObject Command Handler 설치기준 메뉴명령의목적에따라설치 데이터관리 -Document 클라이언트영역관련작업 -View 메인프레임윈도우관련작업 - FrameWnd CCmdTarget CWinApp CDocument CWnd Command Message Handler IDR_MAINFRAME 의메뉴항목에서 Add Event Handler 를선택 CFrameWnd 23 CView 24
Command Message Handler Event Handler Wizard 를이용하여메뉴항목선택시실행되는명령메시지핸들러생성 메뉴명령처리 메뉴명령처리예 BEGIN_MESSAGE_MAP(CChildView,CWnd )... ON_COMMAND(ID_TEXTCOLOR_RED, OnTextColorRed) ON_COMMAND(ID_TEXTCOLOR_GREEN, OnTextColorGreen) ON_COMMAND(ID_TEXTCOLOR_BLUE, OnTextColorBlue)... END_MESSAGE_MAP() void CChildView::OnTextColorRed() m_textcolor = RGB(255, 0, 0); Invalidate(); 25 26 메뉴명령처리 메뉴명령처리예 메뉴항목갱신 메뉴항목갱신예 void CChildView::OnTextColorGreen() m_textcolor = RGB(0, 255, 0); Invalidate(); void CChildView::OnTextColorBlue() m_textcolor = RGB(0, 0, 255); Invalidate(); 27 28
메뉴항목갱신 MFC 의메뉴항목갱신방법 각각의메뉴항목에대해함수를따로작성 명령갱신핸들러 (Command Update Handler) ON_UPDATE_COMMAND_UI( 메뉴ID, 함수명 ) 매크로를이용하여메뉴항목과함수연결 명령라우팅 명령핸들러와마찬가지로명령갱신핸들러도작성하는위치에관계없이처리 Command Update Handler Event Handler Wizard 를이용하여메뉴항목선택시실행되는명령갱신핸들러생성 29 30 메뉴항목갱신 메뉴항목갱신예 BEGIN_MESSAGE_MAP(CChildView,CWnd )... ON_UPDATE_COMMAND_UI(ID_COLOR_RED, OnUpdateTextColorRed) ON_UPDATE_COMMAND_UI(ID_COLOR_GREEN, OnUpdateTextColorGreen) ON_UPDATE_COMMAND_UI(ID_COLOR_BLUE, OnUpdateTextColorBlue)... END_MESSAGE_MAP() // 메뉴항목변경메시지핸들러 void CChildView::OnUpdateTextColorRed(CCmdUI* pcmdui) pcmdui->setcheck(m_textcolor == RGB(255, 0, 0)); 31 메뉴항목갱신 메뉴항목갱신예 void CChildView::OnUpdateTextColorGreen(CCmdUI* pcmdui) pcmdui->setcheck(m_textcolor == RGB(0, 255, 0)); void CChildView::OnUpdateTextColorBlue(CCmdUI* pcmdui) pcmdui->setcheck(m_textcolor == RGB(0, 0, 255)); 32
CCmdUI 클래스 명령을수행하는사용자인터페이스를변경하는클래스 주요메소드 Enable : 메뉴항목활성화 (TRUE)/ 비활성화 (FALSE) SetText : 메뉴항목문자열변경 SetCheck : 메뉴항목에체크표시보이게 (1)/ 보이지않게 (0) 설정 SetRadio : 메뉴항목에원점표시보이게 (1)/ 보이지않게 (0) 설정 멤버함수의미사용예 Enable( ) 활성화상태변경 pcmdui->enable(b_drawmode); SetCheck( ) 체크상태변경 pcmdui->setcheck(m_textcolor == RGB(255, 0, 0)); SetRadio( ) 라디오표시상태변경 pcmdui->setradio(m_textcolor == RGB(255, 0, 0)); SetText( ) 문자열변경 pcmdui->settext("light On"); 33 컨텍스트메뉴 마우스오른쪽버튼을누르면열리는단축메뉴 마우스커서위치, 현재작업에따라다른메뉴항목표시 WM_CONTEXTMENU 메시지발생상황 클라이언트영역또는비클라이언트영역에서마우스오른쪽버튼을클릭하는경우 Shift + F10 키조합을누른경우 가상키코드 VK_APPS 에해당하는키를누른경우 WM_CONTEXTMENU 메시지핸들러 afx_msg void OnContextMenu (CWnd* pwnd, CPoint pos) ; pwnd - 마우스커서아래쪽에있는윈도우 pos - 마우스커서의위치 ( 스크린좌표 ) 34 컨텍스트메뉴 CMenu::TrackPopupMenu() 함수 WM_CONTEXTMENU 메시지발생시팝업메뉴를표시하기위한함수 BOOL TrackPopupMenu (UINT nflags, int x, int y, CWnd* pwnd, LPCRECT lprect = 0) ; nflags: 컨텍스트메뉴의마우스커서위치및선택버튼 TPM_LEFTALIGN, TPM_CENTERALIGN, TPM_RIGHTALIGN 컨텍스트메뉴 x, y 컨텍스트메뉴가표시될위치 ( 스크린좌표 ) pwnd 컨텍스트메뉴에서발생한 WM_COMMAND 메시지를받을윈도우 lprect 마우스버튼을클릭하더라도컨텍스트메뉴가닫히지않는사각형영역 ( 스크린좌표 ) TPM_LEFTBUTTON, TPM_RIGHTBUTTON 35 36
컨텍스트메뉴 컨텍스트메뉴사용예 void CChildView::OnContextMenu(CWnd* pwnd, CPoint point) CMenu menu; menu.loadmenu(idr_mainframe); CMenu* pmenu = menu.getsubmenu(3); pmenu->trackpopupmenu( TPM_LEFTALIGN TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); 0 1 2 4 37 // 새로운메뉴를동적으로생성하여컨텍스트메뉴로사용한예 class CChildView : public CView // Generated message map functions protected: //AFX_MSG(CUIIView) afx_msg void OnContextMenu(CWnd* pwnd, CPoint point); afx_msg void OnAlignmentCenter(); // 메시지핸들러선언 //AFX_MSG DECLARE_MESSAGE_MAP() ; BEGIN_MESSAGE_MAP(CUIView, CView) //AFX_MSG_MAP(CUIIView) ON_WM_CONTEXTMENU() ON_COMMAND(201, OnAlignmentCenter) // 메시지맵 //AFX_MSG_MAP END_MESSAGE_MAP() void CChildView::OnContextMenu(CWnd* pwnd, CPoint point) CMenu menu; menu.createpopupmenu(); // 팝업메뉴생성 menu.appendmenu(mf_string, 201, " 가운데정렬 "); //3 개의메뉴항목추가 menu.appendmenu(mf_string, 202, " 왼쪽정렬 "); menu.appendmenu(mf_string, 203, " 오른쪽정렬 "); // 메뉴를컨텍스트팝업메뉴로표시 menu.trackpopupmenu(tpm_leftalign, point.x, point.y, AfxGetMainWnd()); void CChildView::OnAlignmentCenter() m_textpoint = DT_CENTER ; // 가운데정렬 Invalidate(); 시스템메뉴 윈도우조작과관련된메뉴 이동 / 크기조정 / 최대화 / 최소화 / 닫기 CWnd::GetSystemMenu() 시스템메뉴의 CMenu 포인터를얻기위한함수 CMenu 클래스가제공하는다양한함수 (AppendMenu(), InsertMenu(), DeleteMenu(),...) 를적용 주의사항 시스템메뉴를변경하려면 GetSystemMenu(FALSE) 를, 시스템메뉴를초기상태로되돌리려면 GetSystemMenu(TRUE) 를호출한다. 시스템메뉴에새로운메뉴항목을추가할때메뉴 ID는반드시 16의정수배가되어야한다. 시스템메뉴항목을선택하면 WM_COMMAND가아닌 WM_SYSCOMMAND 메시지가발생한다. 39 시스템메뉴 class CMainFrame : public CFrameWnd rotected: //AFX_MSG(CMainFrame) afx_msg void OnSysCommand(UINT nid, LPARAM lparam); //AFX_MSG DECLARE_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //AFX_MSG_MAP(CMainFrame) ON_WM_SYSCOMMAND() //AFX_MSG_MAP END_MESSAGE_MAP()
int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 시스템메뉴수정 CMenu * psysmenu = GetSystemMenu(FALSE); psysmenu->appendmenu(mf_separator); // 구분선추가 //ID 가 16 의배수가되도록설정 psysmenu->appendmenu(mf_string, 16, " 추가된항목 "); return 0; void CMainFrame::OnSysCommand(UINT nid, LPARAM lparam) // nid 의하위 4 비트는운영체제가사용하므로이를무시하기위해 // 0xFFF0 과 AND 연산하여비교 if ((nid & 0xFFF0) == 16) AfxMessageBox(" 시스템메뉴연습입니다."); return; CFrameWnd::OnSysCommand(nID, lparam); 가속기 가속기 = 단축키 메뉴항목을곧바로실행할수있는키조합 가속기리소스에가속기를만들메뉴항목추가 가속기를누르면 WM_COMMAND 메시지가발생 42 툴바 (Toolbar) 메뉴항목기능을빠르게수행하기위한명령버튼 WM_COMMAND 메시지발생 툴바 툴바를구성하는버튼들이비트맵으로배치 툴바버튼수정 / 추가 / 이동 / 삭제처리 툴바버튼을마우스로더블클릭하여속성설정 MFC 클래스 선택하면 WM_COMMAND 메시지발생! 43 44
툴바 툴바코드 툴바 툴바코드 class CMainFrame : public CFrameWnd protected: CStatusBar m_wndstatusbar; CToolBar m_wndtoolbar; CChildView m_wndview; ; int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) if (!m_wndtoolbar.createex(this, TBSTYLE_FLAT, WS_CHILD 45 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; m_wndtoolbar.enabledocking(cbrs_align_any); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); return 0; 46 상태바 메인프레임하단부에위치하여상태를확인하는윈도우 상태바는팬이라불리는표시영역들로나뉜다 고정되거나변화할수있는크기를갖는다 제일왼쪽의팬은가변크기팬이다 팬번호는왼쪽으로부터 0, 1, 2, 3 부여 MFC 클래스 상태바 상태바리소스 static UINT indicators[ ] = ID_SEPARATOR, ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, ; 47 48
상태바 상태바코드 class CMainFrame : public CFrameWnd public: CStatusBar m_wndstatusbar; protected: CToolBar m_wndtoolbar; CChildView m_wndview; ; // 상태바의멤버변수선언 49 상태바 상태바코드 int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) // 상태바생성후상태바객체와연결 // 상태바의각펜을해당문자열리소스값으로설정 if (!m_wndstatusbar.create(this)!m_wndstatusbar.setindicators(indicators, sizeof(indicators)/sizeof(uint))) TRACE0("Failed to create status bar\n"); return -1; 50 상태바에새로운팬생성단계 팬 (Pane) 을생성 추가할팬의 ID 생성 -[Resource Symbol] 대화상자이용 생성된팬의초기문자열등록 -[String Table] 의빈칸에생성한팬의 ID, 초기문자열입력 상태바에팬을추가 상태바를분할하는지시자설정에새로운팬을추가 static UINT indicators[] = ID_SEPARATOR, ID_INDICATOR_POS; // 새로운팬추가 nindex=1 ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, ; 상태바팬생성예 상태바에새로운팬을추가하여마우스좌표를출력하는프로그램 칸속성지정 int CMainFrame::OnCreate(LPCREATESTRUCT lpcreatestruct) // 생략.. // 상태바 nindex=1 의칸속성조정 m_wndstatusbar.setpaneinfo(1, ID_INDICATOR_POS, SBPS_NORMAL, 120); return 0;
상태바팬생성예 상태바에새로운팬을추가하여마우스좌표를출력하는프로그램 칸을활성화 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_WM_SETFOCUS() ON_UPDATE_COMMAND_UI(ID_INDICATOR_POS, OnUpdateIndicatorPos) END_MESSAGE_MAP() // 상태바 nindex=1 의칸을활성화 void CMainFrame::OnUpdateIndicatorPos(CCmdUI* pcmdui) pcmdui->enable(); 상태바팬생성예 상태바에새로운팬을추가하여마우스좌표를출력하는프로그램 상태바에새로운문자열출력 : SetPanelText() 함수이용 void CChildView::OnMouseMove(UINT nflags, CPoint point) //View 에서 MainFrame 객체의포인터얻기 CMainFrame *pframe = (CMainFrame *)AfxGetMainWnd(); CString strstatus; strstatus.format(" 마우스 X : %d, Y : %d ", point.x, point.y); // 메인프레임상태바의 nindex=1 인팬에문자열설정 pframe->m_wndstatusbar.setpanetext(1, strstatus); CWnd::OnMouseMove(nFlags, point);