TN021: 명령 및 메시지 라우팅
참고 항목
다음 기술 노트는 온라인 설명서에 먼저 포함되어 있었으므로 업데이트되지 않았습니다. 따라서 일부 절차 및 항목은 만료되거나 올바르지 않을 수 있습니다. 최신 정보를 보려면 온라인 설명서 색인에서 관심 있는 항목을 검색하는 것이 좋습니다.
이 참고에서는 일반 창 메시지 라우팅의 고급 항목뿐만 아니라 명령 라우팅 및 디스패치 아키텍처에 대해 설명합니다.
여기에 설명된 아키텍처, 특히 Windows 메시지, 컨트롤 알림 및 명령의 차이점에 대한 일반적인 내용은 Visual C++를 참조하세요. 이 참고에서는 인쇄된 설명서에 설명된 문제에 대해 잘 알고 있으며 매우 고급 항목만 다루고 있다고 가정합니다.
MFC 1.0 명령 라우팅 및 디스패치 기능이 MFC 2.0 아키텍처로 진화
Windows에는 메뉴 명령, 가속기 키 및 대화 제어 알림의 알림을 제공하기 위해 오버로드되는 WM_COMMAND 메시지가 있습니다.
MFC 1.0은 파생 클래스의 명령 처리기(예: "OnFileNew") CWnd
가 특정 WM_COMMAND 대한 응답으로 호출될 수 있도록 하여 이를 기반으로 합니다. 메시지 맵이라는 데이터 구조와 함께 붙어 있어 공간 효율적인 명령 메커니즘이 생성됩니다.
MFC 1.0에서는 명령 메시지에서 컨트롤 알림을 분리하는 추가 기능도 제공했습니다. 명령은 명령 ID라고도 하는 16비트 ID로 표시됩니다. 명령은 일반적으로 메뉴 선택 또는 번역된 액셀러레이터에서 CFrameWnd
시작하여 다양한 다른 창으로 라우팅됩니다.
MFC 1.0은 MDI(다중 문서 인터페이스) 구현에 대해 제한된 의미에서 명령 라우팅을 사용했습니다. (MDI 프레임 창은 활성 MDI 자식 창에 명령을 위임합니다.)
이 기능은 MFC 2.0에서 일반화되고 확장되어 창 개체뿐만 아니라 더 넓은 범위의 개체에서 명령을 처리할 수 있습니다. 메시지를 라우팅하기 위한 보다 공식적이고 확장 가능한 아키텍처를 제공하고 명령의 현재 가용성을 반영하도록 명령 처리뿐만 아니라 메뉴 항목 및 도구 모음 단추와 같은 UI 개체를 업데이트하기 위해 명령 대상 라우팅을 다시 사용합니다.
명령 ID
명령 라우팅 및 바인딩 프로세스에 대한 설명은 Visual C++를 참조하세요. Technical Note 20 에는 ID 이름 지정에 대한 정보가 포함되어 있습니다.
명령 ID에 제네릭 접두사 "ID_"를 사용합니다. 명령 ID는 >= 0x8000. 명령 ID와 ID가 동일한 STRINGTABLE 리소스가 있는 경우 메시지 줄 또는 상태 표시줄에 명령 설명 문자열이 표시됩니다.
애플리케이션의 리소스에서 명령 ID는 여러 위치에 나타날 수 있습니다.
한 STRINGTABLE 리소스에서 메시지 줄 프롬프트와 ID가 동일합니다.
동일한 명령을 호출하는 메뉴 항목에 연결된 메뉴 리소스가 많을 수 있습니다.
GOSUB 명령에 대한 대화 상자 단추의 (ADVANCED)
애플리케이션의 소스 코드에서 명령 ID는 여러 위치에 나타날 수 있습니다.
리소스에 있습니다. 애플리케이션별 명령 ID를 정의하는 H(또는 기타 기본 기호 헤더 파일)
아마도 도구 모음을 만드는 데 사용되는 ID 배열에서
ON_COMMAND 매크로에서
아마도 ON_UPDATE_COMMAND_UI 매크로에서.
현재 MFC에서 명령 ID >가 필요한 유일한 구현은 = 0x8000 GOSUB 대화/명령의 구현입니다.
GOSUB 명령, 대화 상자에서 명령 아키텍처 사용
명령 라우팅 및 사용 명령의 명령 아키텍처는 프레임 창, 메뉴 항목, 도구 모음 단추, 대화 상자 표시줄 단추, 기타 컨트롤 막대 및 명령 또는 컨트롤 ID를 기본 명령 대상(일반적으로 기본 프레임 창)으로 업데이트하도록 디자인된 기타 사용자 인터페이스 요소에서 잘 작동합니다. 해당 기본 명령 대상은 명령 또는 컨트롤 알림을 다른 명령 대상 개체에 적절하게 라우팅할 수 있습니다.
대화 상자(모달 또는 모덜리스)는 대화 상자 컨트롤의 컨트롤 ID를 적절한 명령 ID에 할당하는 경우 명령 아키텍처의 일부 기능을 활용할 수 있습니다. 대화에 대한 지원은 자동이 아니므로 몇 가지 추가 코드를 작성해야 할 수 있습니다.
이러한 모든 기능이 제대로 작동하려면 명령 ID가 = 0x8000 합니다 >. 많은 대화 상자가 동일한 프레임으로 라우팅될 >수 있으므로 공유 명령은 = 0x8000, 특정 대화 상자의 공유되지 않는 IDE는 = 0x7FFF 합니다 <.
단추의 IDC가 적절한 명령 ID로 설정된 일반 모달 대화 상자에 일반 단추를 배치할 수 있습니다. 사용자가 단추를 선택하면 대화 상자의 소유자(일반적으로 기본 프레임 창)는 다른 명령과 마찬가지로 명령을 가져옵니다. 일반적으로 다른 대화 상자(첫 번째 대화의 GOSUB)를 가져오는 데 사용되므로 GOSUB 명령이라고 합니다.
대화 상자에서 함수 CWnd::UpdateDialogControls
를 호출하고 기본 프레임 창의 주소를 전달할 수도 있습니다. 이 함수는 프레임에 명령 처리기가 있는지 여부에 따라 대화 상자 컨트롤을 사용하거나 사용하지 않도록 설정합니다. 이 함수는 애플리케이션의 유휴 루프에서 컨트롤 막대에 대해 자동으로 호출되지만 이 기능을 사용하려는 일반 대화 상자에서 직접 호출해야 합니다.
ON_UPDATE_COMMAND_UI 호출되는 경우
프로그램 메뉴 항목의 사용/검사 상태를 항상 유지 관리하는 것은 계산 비용이 많이 드는 문제가 될 수 있습니다. 일반적인 방법은 사용자가 POPUP을 선택하는 경우에만 메뉴 항목을 사용/검사 것입니다. MFC 2.0 구현 CFrameWnd
은 WM_INITMENUPOPUP 메시지를 처리하고 명령 라우팅 아키텍처를 사용하여 ON_UPDATE_COMMAND_UI 처리기를 통해 메뉴 상태를 확인합니다.
CFrameWnd
또한 WM_ENTERIDLE 메시지를 처리하여 상태 표시줄에서 선택한 현재 메뉴 항목(메시지 줄이라고도 함)을 설명합니다.
Visual C++에서 편집한 애플리케이션의 메뉴 구조는 WM_INITMENUPOPUP 시간에 사용할 수 있는 잠재적인 명령을 나타내는 데 사용됩니다. ON_UPDATE_COMMAND_UI 처리기는 메뉴의 상태 또는 텍스트를 수정하거나 고급 용도(예: 파일 MRU 목록 또는 OLE 동사 팝업 메뉴)를 위해 메뉴를 그리기 전에 메뉴 구조를 실제로 수정할 수 있습니다.
애플리케이션이 유휴 루프에 들어갈 때 도구 모음(및 기타 컨트롤 막대)에 대해 동일한 종류의 ON_UPDATE_COMMAND_UI 처리가 수행됩니다. 제어 막대에 대한 자세한 내용은 클래스 라이브러리 참조 및 기술 참고 31 을 참조하세요.
중첩된 팝업 메뉴
중첩된 메뉴 구조를 사용하는 경우 팝업 메뉴의 첫 번째 메뉴 항목에 대한 ON_UPDATE_COMMAND_UI 처리기가 두 가지 경우에 호출됩니다.
먼저 팝업 메뉴 자체에 대해 호출됩니다. 팝업 메뉴에는 ID가 없으며 팝업 메뉴의 첫 번째 메뉴 항목 ID를 사용하여 전체 팝업 메뉴를 참조하기 때문에 이 작업이 필요합니다. 이 경우 개체의 CCmdUI
m_pSubMenu 멤버 변수는 NULL이 아니고 팝업 메뉴를 가리킵니다.
둘째, 팝업 메뉴의 메뉴 항목을 그리기 직전에 호출됩니다. 이 경우 ID는 첫 번째 메뉴 항목만 참조하고 개체의 CCmdUI
m_pSubMenu 멤버 변수는 NULL이 됩니다.
이렇게 하면 해당 메뉴 항목과 다른 팝업 메뉴를 사용하도록 설정할 수 있지만 일부 메뉴 인식 코드를 작성해야 합니다. 예를 들어 다음 구조가 있는 중첩된 메뉴에서 다음을 수행합니다.
File>
New>
Sheet (ID_NEW_SHEET)
Chart (ID_NEW_CHART)
ID_NEW_SHEET 및 ID_NEW_CHART 명령은 독립적으로 사용하거나 사용하지 않도록 설정할 수 있습니다. 두 가지 중 하나를 사용하는 경우 새 팝업 메뉴를 사용하도록 설정해야 합니다.
ID_NEW_SHEET 대한 명령 처리기(팝업의 첫 번째 명령)는 다음과 같습니다.
void CMyApp::OnUpdateNewSheet(CCmdUI* pCmdUI)
{
if (pCmdUI->m_pSubMenu != NULL)
{
// enable entire pop-up for "New" sheet and chart
BOOL bEnable = m_bCanCreateSheet || m_bCanCreateChart;
// CCmdUI::Enable is a no-op for this case, so we
// must do what it would have done.
pCmdUI->m_pMenu->EnableMenuItem(pCmdUI->m_nIndex,
MF_BYPOSITION |
(bEnable MF_ENABLED : (MF_DISABLED | MF_GRAYED)));
return;
}
// otherwise just the New Sheet command
pCmdUI->Enable(m_bCanCreateSheet);
}
ID_NEW_CHART 대한 명령 처리기는 일반적인 업데이트 명령 처리기이며 다음과 같이 표시됩니다.
void CMyApp::OnUpdateNewChart(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bCanCreateChart);
}
ON_COMMAND 및 ON_BN_CLICKED
ON_COMMAND 및 ON_BN_CLICKED 대한 메시지 맵 매크로는 동일합니다. MFC 명령 및 제어 알림 라우팅 메커니즘은 명령 ID만 사용하여 라우팅할 위치를 결정합니다. 컨트롤 알림 코드가 0(BN_CLICKED)인 컨트롤 알림은 명령으로 해석됩니다.
참고 항목
실제로 모든 제어 알림 메시지는 명령 처리기 체인을 통과합니다. 예를 들어 기술적으로 문서 클래스에서 EN_CHANGE 대한 컨트롤 알림 처리기를 작성할 수 있습니다. 이 기능의 실제 응용 프로그램은 거의 없으며, 이 기능은 ClassWizard에서 지원되지 않으며, 기능을 사용하면 코드가 취약할 수 있으므로 일반적으로 권장되지 않습니다.
단추 컨트롤의 자동 사용 안 을 사용하지 않도록 설정
CWnd::UpdateDialogControls를 직접 호출하는 위치를 사용하여 대화 상자나 대화 상자에 단추 컨트롤을 배치하는 경우 ON_COMMAND 또는 ON_UPDATE_COMMAND_UI 처리기가 없는 단추는 프레임워크에서 자동으로 비활성화됩니다. 경우에 따라 처리기가 필요하지 않지만 단추를 다시 사용하도록 설정할 기본. 이 작업을 수행하는 가장 쉬운 방법은 더미 명령 처리기(ClassWizard로 쉽게 수행할 수 있음)를 추가하고 아무 작업도 수행하지 않는 것입니다.
창 메시지 라우팅
다음은 MFC 클래스에 대한 몇 가지 고급 항목과 Windows 메시지 라우팅 및 기타 항목이 이러한 항목에 미치는 영향에 대해 설명합니다. 여기에 있는 정보는 간략하게 설명되어 있습니다. 공용 API에 대한 자세한 내용은 클래스 라이브러리 참조를 참조 하세요. 구현 세부 정보에 대한 자세한 내용은 MFC 라이브러리 소스 코드를 참조하세요.
모든 CWnd 파생 클래스에 대한 매우 중요한 항목인 Window 클린up에 대한 자세한 내용은 Technical Note 17을 참조하세요.
CWnd 문제
구현 멤버 함수 CWnd::OnChildNotify 는 자식 창(컨트롤이라고도 함)에 대한 강력하고 확장 가능한 아키텍처를 제공하여 부모(또는 "소유자")로 이동하는 메시지, 명령 및 컨트롤 알림을 후크하거나 알림을 받을 수 있습니다. 자식 창(/control)이 C++ CWnd 개체 자체인 경우 원본 메시지(즉, MSG 구조)의 매개 변수를 사용하여 OnChildNotify 가상 함수가 먼저 호출됩니다. 자식 창은 메시지를 그대로 두거나, 먹거나, 부모에 대한 메시지를 수정할 수 있습니다(드문 경우).
기본 CWnd 구현은 다음 메시지를 처리하고 OnChildNotify 후크를 사용하여 자식 창(컨트롤)이 메시지에서 먼저 액세스하도록 허용합니다.
WM_MEASUREITEM 및 WM_DRAWITEM(자체 그리기용)
WM_COMPAREITEM 및 WM_DELETEITEM(자체 그리기용)
WM_HSCROLL 및 WM_VSCROLL
WM_CTLCOLOR
WM_PARENTNOTIFY
OnChildNotify 후크가 소유자 그리기 메시지를 자체 그리기 메시지로 변경하는 데 사용됩니다.
OnChildNotify 후크 외에도 스크롤 메시지에는 추가 라우팅 동작이 있습니다. 스크롤 막대 및 WM_HSCROLL 및 WM_VSCROLL 메시지 원본에 대한 자세한 내용은 아래를 참조하세요.
CFrameWnd 문제
CFrameWnd 클래스는 대부분의 명령 라우팅 및 사용자 인터페이스 업데이트 구현을 제공합니다. 이는 주로 애플리케이션의 기본 프레임 창(CWinApp::m_pMainWnd)에 사용되지만 모든 프레임 창에 적용됩니다.
기본 프레임 창은 메뉴 모음이 있는 창이며 상태 표시줄 또는 메시지 줄의 부모입니다. 명령 라우팅 및 WM_INITMENUPOPUP 대한 위의 설명을 참조하세요.
CFrameWnd 클래스는 활성 뷰의 관리를 제공합니다. 다음 메시지는 활성 보기를 통해 라우팅됩니다.
모든 명령 메시지(활성 보기는 먼저 액세스 권한을 얻습니다).
형제 스크롤 막대에서 메시지를 WM_HSCROLL WM_VSCROLL(아래 참조).
WM_ACTIVATE(및 MDI용 WM_MDIACTIVATE)는 가상 함수 CView::OnActivateView에 대한 호출로 바꿉니다.
CMDIFrameWnd/CMDIChildWnd 문제
두 MDI 프레임 창 클래스는 모두 CFrameWnd에서 파생되므로 CFrameWnd 에서 제공하는 동일한 종류의 명령 라우팅 및 사용자 인터페이스 업데이트에 대해 사용하도록 설정됩니다. 일반적인 MDI 애플리케이션에서는 기본 프레임 창(즉, CMDIFrameWnd 개체)만 메뉴 모음과 상태 막대를 보유하므로 명령 라우팅 구현의 기본 원본입니다.
일반적인 라우팅 체계는 활성 MDI 자식 창이 명령에 먼저 액세스한다는 것입니다. 기본 PreTranslateMessage 함수는 MDI 자식 창(첫 번째) 및 MDI 프레임(두 번째)과 TranslateMDISysAccel(마지막)에서 일반적으로 처리하는 표준 MDI 시스템 명령 가속기 모두에 대한 가속기 테이블을 처리합니다.
스크롤 막대 문제
OnHScroll 및/또는 WM_VSCROLL/OnHScroll과 WM_HSCROLL/스크롤 메시지를 처리할 때는 스크롤 막대 메시지가 어디에서 왔는지에 의존하지 않도록 처리기 코드를 작성해야 합니다. 스크롤 메시지는 실제 스크롤 막대 컨트롤 또는 스크롤 막대 컨트롤이 아닌 WS_HSCROLL WS_VSCROLL/ 스크롤 막대에서 올 수 있으므로 일반적인 Windows 문제일 뿐만 아니라
MFC는 스크롤 막대 컨트롤이 스크롤되는 창의 자식 또는 형제가 되도록 확장합니다(실제로 스크롤 막대와 스크롤되는 창 간의 부모/자식 관계는 무엇이든 될 수 있음). 분할자 창이 있는 공유 스크롤 막대에 특히 중요합니다. 공유 스크롤 막대 문제에 대한 자세한 내용을 포함하여 CSplitterWnd 구현에 대한 자세한 내용은 Technical Note 29를 참조하세요.
참고로, 만들기 시간에 지정된 스크롤 막대 스타일이 트래핑되어 Windows에 전달되지 않는 두 개의 CWnd 파생 클래스가 있습니다. 만들기 루틴 에 전달되면 WS_HSCROLL 및 WS_VSCROLL 독립적으로 설정할 수 있지만 만든 후에는 변경할 수 없습니다. 물론 만든 창의 WS_SCROLL 스타일 비트를 직접 테스트하거나 설정해서는 안 됩니다.
CMDIFrameWnd의 경우 Create 또는 LoadFrame에 전달하는 스크롤 막대 스타일을 사용하여 MDICLIENT를 만듭니다. 스크롤 가능한 MDICLIENT 영역(예: Windows 프로그램 관리자)을 사용하려면 CMDIFrameWnd를 만드는 데 사용되는 스타일에 대해 두 스크롤 막대 스타일(WS_HSCROLL | WS_VSCROLL
)을 모두 설정해야 합니다.
CSplitterWnd의 경우 스크롤 막대 스타일은 분할자 영역에 대한 특별한 공유 스크롤 막대에 적용됩니다. 정적 분할기 창의 경우 일반적으로 스크롤 막대 스타일을 설정하지 않습니다. 동적 분할자 창의 경우 일반적으로 분할할 방향에 대해 스크롤 막대 스타일이 설정됩니다. 즉, 행을 분할할 수 있는 경우 WS_HSCROLL 열을 분할할 수 있는 경우 WS_VSCROLL.