대화상자
1. 대화상자의 종류
1) 모달(Modal) 대화상자
모달은 대화상자를 닫기 전에는 다른 윈도우로 전환할 수 없으며 반드시 OK 버튼이나 Cancel 버튼을 눌러 대화상자를 닫아야만 다른 윈도우로 전환되는 대화상자를 의미합니다.
모달 대화상자는 CDialog 객체로 DoModal() 함수를 호출해서 생성하고 EndDialog() 함수로 종료합니다.
2) 모들리스(Modeless) 대화상자
모들리스형은 대화상자를 열어 놓은 채로 다른 작업을 할 수 있는 대화상자입니다.
프로그램의 상태를 나타내거나 작업을 하면서 참조해야 할 여러 가지 정보를 보여주는 대화상자가 모들리스형으로 만들어집니다.
그러나 모들리스형은 다른 작업을 하면서도 열려 있을 수 있기 때문에 훨씬 더 프로그래밍하기가 까다롭다는 단점이 있습니다.
Create()를 이용해서 생성하고 Destroy()함수를 이용해서 직접 파괴해야 하며 이미 열려있다면 다시 열리지 않고 화면에 표시되게만 해야 합니다.
3) 공통 대화상자
자주 사용하는 대화상자를 미리 만들어 두고 이를 호출만으로 사용할 수 있는 대화상자
2. 모달 대화상자 만들기
모달 대화상자는 CDialog 클래스의 DoModal() 멤버 함수에 의해 호출되고 EndDiaglog() 함수에 의해 종료됩니다.
예제) 좌표와 문자열을 입력 받아서 뷰에 출력하는 프로젝트
3. 모델리스 대화상자 만들기
모달 대화상자는 DoModal 함수 내부에서 모든 처리를 다 수행해 주기 때문에 상대적으로 프로그래밍하기가 더 쉽습니다.
모델리스 대화상자는 열어 놓은 채로 사용하기 때문에 여러 가지 골치 아픈 문제들이 발생하게 됩니다.
우선 대화상자를 열어 놓고도 계속 다른 작업을 할 수 있어야 하므로 메뉴 항목 등의 메시지 핸들러 함수 내에서 대화 상자를 만들고 파괴하는 모든 일을 다 할 수 없으며, 만드는 과정과 파괴하는 과정이 분리되어야 합니다.
대화상자를 닫지 않고도 OK 버튼을 누르지 않고도 변경한 값이 즉각 반영되도록 해 주어야 합니다.
다이얼로그 객체의 GetSafeHwnd()함수를 이용해서 현재 이 윈도우 핸들이 살아있는지 확인을 먼저 합니다.
이 함수는 윈도우 핸들이 살아 있으면 핸들을 리턴하고 핸들이 없으면 NULL을 리턴합니다.
만일 살아있다면 ShowWindow()를 호출해서 보여주면 되고 살아있지 않다면 Create함수를 호출해서 다이얼로그를 생성해야 합니다.
Create에게는 다이얼로그의 리소스ID를 넘겨주면 됩니다.
Create로 대화상자를 만든 후에는 ShowWindow 함수로 대화상자가 화면에 나타나도록 해 주어야 합니다.
Create는 메모리상으로만 대화상자를 만들 뿐 대화상자를 화면으로 출력해 주는 것은 아닙니다.
대화상자의 속성 중에 Visible 속성이 설정되어 있으면 Create 함수로 대화상자를 만들자 마자 화면으로도 출력해줍니다.
앞의 예제에 모들리스 대화상자 추가하기
4. 메시지 맵 수정
1. 메시지 맵과 메시지 맵 레인지 (Message-Map Ranges)
클래스 위저드가 만들어준 소스를 보면 첫 부분에 메시지 맵이 있습니다.
이것은 클래스가 받는 메시지들과 그 메시지들을 처리하는 함수들을
보통 하나의 명령 메시지를 처리하기 위해서 하나의 핸들러 함수를 만들게 되지만 그렇게 만든 함수들이 같은 방식으로 동작한다면 대부분의 소스코드의 구성이 비슷하다면 여러 개의 메시지 핸들러 함수들을 따로 둘 필요 없이 하나의 핸들러 함수에서 모두 처리하는 것이 좋을 수도 있다.
메시지 맵 레인지는 하나의 메시지에 하나의 함수를 대응시키는 대신에 일정 범위의 메시지를 몽땅 하나의 함수에 대응시켜 두어 처리하는 방식입니다.
명령 메시지와 명령 업데이트 메시지는 물론 컨트롤들이 받는 메시지(notification message)까지도 처리할 수 있는데 각각 ON_COMMAND_RANGE, ON_UPDATE_COMMAND_UI_RANGE, ON_CONTROL_RANGE 매크로가 사용되며 소스에 있는 메시지 맵을 프로그래머가 직접 수정해 주어야 합니다.
클래스 위저드는 이들 매크로의 작성을 도와주지 않기 때문입니다.
2. 매크로의 소개
ON_COMMAND_RANGE( id1, id2, memberFxn ) ON_UPDATE_COMMAND_UI_RANGE( id1, id2, memberFxn ) ON_CONTROL_RANGE( wNotifyCode, id1, id2, memberFxn ) |
id1: 연속되는 범위의 시작 명령 ID (ON_CONTROL_RANGE의 경우엔 컨트롤 ID)
id2: 연속되는 범위의 마지막 명령 ID (ON_CONTROL_RANGE의 경우엔 컨트롤 ID)
memberFxn: 메시지를 처리할 함수
wNotifyCode: notification 메시지 시작과 마지막이란 얘기는 명령 ID 심볼에 부여된 상수 값의 크기를 의미하는데 정확히 말하자면 그 상수 값이 가장 작은 명령부터 가장 큰 명령까지란 의미입니다.
3. ON_COMMAND_RANGE 매크로
① 각각의 메시지가 하나씩 핸들러를 가질 경우;
사각형을 그리는 ID_DRAW_RECT 명령과 동그라미를 그리는 ID_DRAW_CIRCLE, 삼각형을 그리는 ID_DRAW_TRIANGLE 명령 ID들을 만들고 각각의 핸들러를 두었다면 메시지 맵은 다음과 같을 것입니다.
BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CEcrDoc) ON_COMMAND(ID_DRAW_RECT, OnRect) ON_COMMAND(ID_DRAW_CIRCLE, OnCircle) ON_COMMAND(ID_DRAW_TRIANGLE, OnTriangle) //}}AFX_MSG_MAP END_MESSAGE_MAP() |
소스엔 OnRect(), OnCircle(), OnTriangle() 세 개의 핸들러가 만들어졌을 테고, 이 함수들의 원형은 헤더 파일에 다음처럼 선언되어 있을 것입니다.
// Generated message map functions protected: //{{AFX_MSG(CEcrDoc) afx_msg void OnRect(); afx_msg void OnCircle(); afx_msg void OnTriangle(); //}}AFX_MSG |
② 메시지 맵의 수정;
메시지 맵에서 "//}}AFX_MSG_MAP" 다음 줄에 매크로를 작성합니다.
BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) //}}AFX_MSG_MAP ON_COMMAND_RANGE(ID_RECT, ID_TRIANGLE, OnDrawFigure) END_MESSAGE_MAP() |
이렇게 함으로써 ID_RECT 부터 ID_TRIANGLE 범위에 드는 메시지가 발생하면 OnDrawFigure()가 호출됩니다.
③ 헤더의 수정
// Generated message map functions protected: //{{AFX_MSG(CDrawView) //}}AFX_MSG afx_msg void OnDrawFigure(UINT nID); |
④ OnDrawFigure()의 구성;
함수가 호출되면 우선 어떤 메시지를 처리해야 하는가를 판단해야 합니다.
가장 흔한 방법이 switch 문의 사용이며 다음과 같은 형식이 될 것입니다.
void CDrawView::OnDrawFigure( UINT nID ) { switch ( nID ) { case ID_RECT: ... break;
case ID_CIRCLE: ... break;
case ID_TRIANGLE: ... break; } } |
물론 처리할 메시지가 많지 않다면 if 문을 사용하는 게 좋을 수도 있습니다.
4. ON_UPDATE_COMMAND_UI_RANGE 매크로
ON_COMMAND_RANGE 매크로와 크게 다르지 않습니다.
/* 헤더파일 */ // Generated message map functions protected: //{{AFX_MSG(CDrawView) //}}AFX_MSG afx_msg void OnUpdateDrawFigure(CCmdUI* pCmdUI);
/* 메시지 맵 */ BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) //}}AFX_MSG_MAP ON_UPDATE_COMMAND_UI_RANGE(ID_RECT, ID_TRIANGLE, OnUpdateDrawFigure) END_MESSAGE_MAP() void CDrawView::OnUpdateDrawFigure( CCmdUI* pCmdUI ) { switch ( pCmdUI->m_nID ) { case ID_RECT: ... break; case ID_CIRCLE: ... break; case ID_TRIANGLE: ... break; } } |
UPDATE_COMMAND_UI 메시지 핸들러의 경우엔 함수의 인자로 명령 ID가 전달되지 않습니다. 그러나 CCmdUI 클래스의 데이터 멤버 중엔 m_nID가 있고 이것으로부터 명령 ID를 구하면 됩니다.
5. ON_CONTROL_RANGE 매크로
예를 들어 대화상자에 버튼 컨트롤을 여러 개 두었고 각각의 버튼을 눌렀을 때 처리할 작업이 있다면 버튼들에 대해 BN_CLICKED 메시지 핸들러를 작성해야 합니다.
이와 같은 경우 모든 버튼의 Notify 메시지를 하나의 핸들러에서 처리하려면 ON_CONTROL_RANGE 매크로를 사용합니다.
사용 예는 다음과 같습니다.
/* 헤더파일 */
// Generated message map functions //{{AFX_MSG(CMyDlg) //}}AFX_MSG afx_msg void OnBtnClicked(UINT nID);
/* 메시지 맵 */
BEGIN_MESSAGE_MAP(CMyDlg, CDialog) //{{AFX_MSG_MAP(CMyDlg) //}}AFX_MSG_MAP ON_CONTROL_RANGE( BN_CLICKED, IDC_BTN1, IDC_BTN5, OnBtnClicked) END_MESSAGE_MAP()
void CMyDlg::OnBtnClicked( UINT nID ) { switch ( nID ) { case IDC_BTN1: ... } } |
예제) 여러 개의 라디오 버튼 메시지를 묶는 방법
4. 공통 대화상자
공통대화상자(Common Dialog)란 말 그대로 모든 윈도우용 프로그램들이 공통으로 사용하는 대화상자입니다.
자주 사용되기 때문에 이 대화상자들은 아예 윈도우가 운영체제 차원에서 제공해 주므로 응용프로그램들은 윈도우의 서비스만 받으면 이 대화상자를 쉽게 사용할 수 있습니다.
공통 대화상자와 클래스
대화상자 |
클래스 |
파일 열기 대화상자 |
CFileDialog |
폰트 선택 대화상자 |
CFontDialog |
색상 선택 대화상자 |
CColorDialog |
페이지 셋업 대화상자 |
CPageSetupDialog |
인쇄 대화 상자 |
CPrintDialog |
찾기, 바꾸기 대화상자 |
CFindReplaceDialog |
찾기와 바꾸기 대화상자를 제외하면 전부 모달 대화상자입니다.
파일열기 대화상자의 생성자에게 TRUE를 넘기면 열기가 되고 FALSE를 넘기면 다른 이름으로 저장 대화상자가 되면 페이지 셋업 대화상자도 생성자에게 넘겨주는 인수에 따라 페이지 셋업이나 프린터 셋업이 되며 찾기와 바꾸기도 마찬가지 입니다.
CFileDialog의 주요 멤버 함수
DoModal() |
파일 열기/저장 공통 대화 상자를 표시함 |
파일의 경로를 반환 | |
파일의 이름을 반환 | |
선택된 파일의 이름을 반환 | |
경로와 확장자를 제외한 이름을 반환 | |
다중으로 선택 된 파일의 이름을 반환 | |
읽기 전용으로 선택 | |
여러 개 선택 된 파일 중 첫 번째 파일의 위치를 반환 |
CFontDialog의 주요 멤버 함수
DoModal() |
글꼴 공통 대화 상자를 표시함 |
선택된 글꼴의 문자 포맷을 구함 | |
글꼴의 색상을 구함 | |
선택된 글꼴의 속성을 구함(LOGFONT 구조체 이용) | |
선택된 글꼴의 이름을 구함 | |
글꼴의 크기를 구함 | |
글꼴의 스타일 구함 | |
글꼴의 Weight를 구함 | |
볼드체 여부 | |
이탤릭체 여부 | |
취소선 여부 |
'C & C++ > C & C++' 카테고리의 다른 글
[MFC] DLL( Dynamic Linking Library) (0) | 2011.04.25 |
---|---|
[MFC] 파일 입출력 (0) | 2011.04.25 |
[MFC] Message 처리 (0) | 2011.04.25 |
[MFC] 계층적 구조 (0) | 2011.04.25 |
[MFC] 주요 클래스 (0) | 2011.04.25 |
댓글