Using the ATL Windowing Classes


이글은 코드구루의 내용을 연습삼아 번역한 것이라서 번역은 엉망이다.


요즘 ATL 이란 놈이 머리를 아프게 한다...

ATL은 배우는 것은 힘들지만 MFC보다 작기 때문에 배우는데 짧은 기간이 걸린다고 한다.

근데 힘들다는 것은 그만큼 오래 걸린다는 것고 같은 의미라고 느껴진다.

세상에 쉬운것은 없군...

ATL 은 COM을 지원하기 위해 디자인 되었지만 윈도우를 모델링 하는 클래스 영역도 포함한다고

한다. 그리고 ActiveX 같은 윈도우를 가지는 객체도 만들수 있다.

아래는 ATL 에서의 주요 윈도우 클래스들이다.


CWindow - 윈도우를 조작하기 위한 Win32 APIs의 작은 랩퍼 클래스이다.

윈도우 핸들과 HWND 를 CWindow 로 변환하는 오퍼레이터를 포함한다.

그러므로 윈도우 핸들을 필요로하는 어떤 함수에 CWindow 오브젝트를

넘길수 있다.

CWindowImpl - 이미 존재하는 윈도우를 서브클래싱 하거나 이미 존재하는 클래스를

수퍼클래싱 하거나 , 윈도우 베이스의 새로운 윈도우를 만들때

사용한다.
CContainedWindow - 다른 클래스의 메세지 맵을 위한 메세지 경로를 구현한 윈도우

클래스이다. 이 클래스는 하나의 클래스에 메세지 처리를 집중하는 것을 허락한다.

CAxWindow - 컨트롤을 만들거나 존재 하는 컨트롤에 붙임으로써 ActiveX control

호스트 윈도우 구현을 지원한다.
CDialogImpl - 모달이나 모달리스 다이얼로스를 구현한다. IDOK 나 IDCANCEL 같은

기본 메세지 경로를 지원한다.
CSimpleDialog - 단순 모달 다이얼로그를 주어진 리소스 ID로 구현한다. IDOK나 IDCANCEL과

같은 기본 메세지 맵을 기지고 있다.
CAxDialogImpl - CDialogImpl 과 같이 모달과 모달리스를 를 구현하는 베이스 클래스로 사용되

며 상속된 클래스에 기본 메세지 맵을 제공한다.
추가로 ActiveX 컨트롤을 지원한다. ATL 오브젝트 위저드에서 CAxDialogImpl에

상속된 클래스를 프로젝트에 넣은 것을 지원한다.

CWndClassInfo - 새로운 윈도우 클래스의 정보를 보관한다.

특별히 WNDCLASSEX를 캡슐화한다.

CWndTraits and CWinTraitsOR - ATL 윈도우 오브젝트의 스타일을 캡슐화한다.

Message Maps

ATL을 공부하는데 시간을 투자하는 것이 꺼려지는 이유 중 하나가 괴상한 메세지 맵이다.
그러나 MFC의 메세지의 매크로를 처음 봤을 때 이해가 됬는가?
사실 ATL의 맵은 더 쉽다....(과연 그럴까?)

베이스 추상클래스의 CMessageMap 으로부터의 상속과 CWindowImpl 에서 상속된 클래스들은

윈도우 메세지들을 처리할 수 있도록 해준다.
CMessageMap 에 순수 가상함수로 정의 된 ProcessWindowMessage 는 CWindowImpl에서

상속된 클래스의 BEGION_MSG_MAP 과 END_MSG_MAP 매크로를 통해 구현된다.

ATL은 MFC와 유사한 포멧의 메세지 핸들러 가졌고 추가적으로 BOOL& 형의 아규먼트를 받는다.
이 아규먼트는 메세지가 진행중인지 아닌지를 나타내고 TURE 가 기본값이다.

FALSE 은 메세지를 가지고 있지 않다는 것이다.
FALSE인 경우 ATL 은 메세지 맵에서 보다 먼 핸들러 함수를 찾는다.

FLASE를 셋팅함으로 어떤 액션을 가 할 수 있는데 기본 프로세싱을
허용하거나 메세지 핸들링을 끝내기 위해 다른 핸들러 함수를 허용할 수 있다.


메세지 맵의 매크로는 다음과 같이 3가지 있다.
1. 모든 메세지를 위한 메세지 핸들러
2. WM_COMMAND 메세지를 위한 커맨드 핸들러
3. WM_NOTIFY 메세지를 위한 통지 핸들러

MESSAGE_HANDLER 핸들러 함수를 위한 윈도우 메세지 맵
COMMAND_HANDLER 메뉴 나 통지 코드, 컨트롤, 단축키의 ID 에 기초한 WM_COMMAND 메세지 핸들러 함수의 메세지 맵
COMMAND_ID_HANDLER 메뉴 , 컨트롤, 단축키의 ID 에 기초한 WM_COMMAND 메세지 핸들러 함수의 메세지 맵
COMMAND_CODE_HANDLER 통지 코드에 기초한 WM_COMMAND 메세지 핸들러 맵
NOTIFY_HANDLER 컨트롤 식별자나 통지코드에 기초한 WM_NOTIFY 메세지 핸들러
NOTIFY_ID_HANDLER 컨트롤 식별자에 기초한 WM_NOTIFY 메세지 핸들러
NOTIFY_CODE_HANDLER 통지 코드에 기초한 WM_NOTIFY 메세지 핸들러

예로 ATL 다이얼로그 폼에서 클래스에서 자식 컨트롤를 가진다면 아래와 같이 메세지 맵이 보여진다.

BEGIN_MSG_MAP(CMyDialog) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1) COMMAND_ID_HANDLER(IDOK, OnOK) COMMAND_CODE_HANDLER(EN_ERRSPACE, OnErrEdits) NOTIFY_HANDLER(IDC_LIST1, NM_CLICK, OnClickList1) NOTIFY_ID_HANDLER(IDC_LIST2, OnSomethingList2) NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDblClkLists) END_MSG_MAP()
MFC 아키텍쳐에서는 서로 다른 메세지 스키마 루틴의 사용도 허락한다. 
:(상위 계층을 통한 윈도우 메세지 루틴과 가로지는 doc-view 클래스를
가로지는 커맨드 메세지)
첫 스키마는 탬플릿 클래스 구현의 부분적인 느슨한 계층을 가진 ATL 에서는
사용할 수 없다.
두번째 스키마도 MFC 아키텍쳐와 동등하지 않기 때문에 적절치 않다
.
(...먼소린지.. @@;)

Alternate Message Maps

Alternate Message Map은 CContainedWindow 를 통해 사용하도록 디자인 되었다.

이 클래스는 다른 클래스로 메세를 보낼때 쓰여진다.
이는 부모 윈도우에 의해 자식 윈도우로 메세지를 보내는 것도 허락된다.
CContainedWindow 생성자는 메세지 맵의 주소를 필요로 한다. 그리고 메세지 맵내에
선택적 메세지맵의 ID를 필요로 한다.
예로, 윈도우 컨트롤에 기초한 ATL 컨트롤을 만들 때 객체 마법사는 CContainedWindow 멤버가
끼워 넣어진 컨트롤을 위한 클래스를 생성한다.
본질적으로, 이 컨테이너 윈도우는 ActiveX control 에 기초한 특유의 윈도우 컨트롤 슈버클래싱
한다.

class ATL_NO_VTABLE CMyButton : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMyButton, &CLSID_MyButton>, public CComControl<CMyButton>, //... { public: CContainedWindow m_ctlButton; CMyButton() : m_ctlButton(_T("Button"), this, 1) { } BEGIN_MSG_MAP(CMyButton) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) CHAIN_MSG_MAP(CComControl<CMyButton>) ALT_MSG_MAP(1) END_MSG_MAP() //...
버튼은 WNDCLASS 스타일이고 캡션이 없다. 이 포함 클래스의 포인터는 두가지 파라미터를
보내고 , 그리고 첫변째 값은 CContainedWindow 생성자 alternate messge map에
대한 생성자가 넘어온다.
만약 컨트롤의 WM_LBUTTONDOWN을 핸들링 하기 원한다면 아래와 같이 메세지 맵을

업데이트 하면 된다. 이와 같은 벙법은 부모윈도우의 메세지맵에 메세지가 보내어 지고,
메세지 맵의 선택적 부분에도 보내어 진다.





BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()

그래서 alternate message map들은 메세지 핸들러들을 통합하기 위한 전략이다.

Chained Message Maps

Chaining message maps 은 다른 클래스나 오프젝트안의 메세지 맵을 통해 메세지를 보낸다. ATL은 몇몇 map - chaining 매크로를 제공한다.

CHAIN_MSG_MAP(theBaseClass)베이스 클래스의 기본 메세지 맵에 메세지를 보낸다.
CHAIN_MSG_MAP_ALT(theBaseClass, mapID)베이스 클래스의 alternate 메세지 맵에 메세지를 보낸다.
CHAIN_MSG_MAP_MEMBER(theMember)데이터 맴버로 명시된 기본 메세지 맵에 메세지를 보낸다.(CMessageMap에서 상속된)
CHAIN_MSG_MAP_ALT_MEMBER(theMember, mapID)명시된 데이터 맴버의 alternate 메세지 맵에 메세지를 보낸다.

예로, 윈도우 컨트롤에 기초한 ATL 컨트롤을 만들 때 오브젝트 마법사는 다음과 같은 코드를 작성
한다.

BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
END_MSG_MAP()

이 WM_CREATE 와 WM-SETFOCUS 메세지의 명세는 이 클래스에 의해서 조정될 것이다.
그러나 어떤 다른 메세지는 베이스 클래스인 CComControl<> 에 보내어질 것이다.
또한 이 메세지 핸들에 false 가 지정되어 있다면 이들 메세지는 더 먼 핸들링을 위해
베이스 클래스를 지나갈 것이다.
데이터 맴버에 보낸 메세지는 이와 같이 맵을 업데이트 해야 한다.

BEGIN_MSG_MAP(CMyButton) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) CHAIN_MSG_MAP(CComControl<CMyButton>) ALT_MSG_MAP(1) CHAIN_MSG_MAP_MEMBER(m_ctlButton) END_MSG_MAP()
이것은 m_ctlButton 이 컨테이너 윈도우의 멤버이라는 가정하이고,
CContainedwindow 에서 상속받은 클래스의 인스턴스이다는 전제하에 이다.
class CMyButtonControl : public CContainedWindow { //... BEGIN_MSG_MAP(CMyButtonControl) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) END_MSG_MAP() 

그래서, chained 메세지 맵은 다른 메세지 맵으로 부터의 chain - route 메세지들을 허락한다.
MFC에 의 변환된 스키마를 보내는 메세지 개념과 유사하다.

Reflected Messages

부모 윈도우는 reflected 메세지로서 메세지를 뒤로 보내는 것에 의해 자식 컨트롤에게 메세지 를 보낸것을 조정할 수 있다.
이는 원본 메세지 + 플래그 일 것이다. 컨트롤이 메세지를 얻었을 때 컨테이너로 부터 반사된 것으로서 식별이 가능하다.
예로 자식 컨트롤에서 WM_DRAWITEM 메세지를 조정하고 싶을 때이다.
이와 같은 작업을 위해 REFLECT_NOTIFICATIONS 는 반드시 부모 윈도의 메세지맵에 나타난다.
BEGIN_MSG_MAP(CMyDialog) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDOK, OnOk) NOTIFY_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1) REFLECT_NOTIFICATIONS() END_MSG_MAP()
REFLECT_NOTIFICATIONS 매크로는 CWindowImpl::ReflectNodifications를
호출하기 위해 확장한다.
template <class TBase> LRESULT CWindowImplRoot<TBase>::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
함수는 메세지르 보내는 자식 컨트롤 을 위한 윈도우 핸들을 추출한다.
그리고 메세지를 아래와 같이 보낸다.
::SendMessage(hWndChild, OCM_ _BASE + uMsg, wParam, lParam);

자식 윈도우 reflected message 핸들들DMS
MESSAGE_HANDLER 매크로 와 olectrl.h에 미리 정의된 reflected 메세지 IDs

를 사용한다.

BEGIN_MSG_MAP(CMyContainedControl) MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP()

이 예제에서 보여진 OCM_DRAWITEM 은 다음과 같이 정의 되어 있다.
#define OCM_ _BASE (WM_USER+0x1c00) #define OCM_COMMAND (OCM_ _BASE + WM_COMMAND) //... #define OCM_DRAWITEM (OCM_ _BASE + WM_DRAWITEM)

DEFAULT_REFLECTION_HANDLER 매크로는 원본 메세지와 DefWindowProc를 지나온 메세지를 변환한다.

Recipe 1: ATL Window App

이것은 ATL Window 클래스를 이용해 단순한 어플리케이션을 쉽게 만들 목적으로
디자인된 단순한 예제이다.

ATL Com AppWizard 는 COM 오브젝트를 위한 호스트를 제공하기 위해 디지인 되어 있다.
만약 non-COM 어플리케이션 이라면 위저드는 더 많은 것을 필요로한다.
그래서 ATL 어플리켄이션을 만들기 위해 두가지 선택이 필요하다.

1. ATL COM AppWizard EXE server
2. 수동적으로 추가해 주는 Win32 Application

여기서는 위저드가 생성한 코드를 피하고 가벼운 두번째 방법으로 따르겠다.
1. Win32 Application 을 Simple option 으로 생성
---ATL
2. stdafx.h에 ATL 헤더와 외부 참조 CComModule 를 추가한다.
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <atlwin.h>

3. main CPP 에 ATL 객체 맵을 추가하고 CComModule 오브젝트를 정의한다.
CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()

4. 프로젝트와 같은 이름의 IDL 파일을 생성한다.
library SomethigOrOther
{
};
--Window
5. CWindowImpl<CMyWidnow> 를 베이스 클래스로 한 CMyWindow를 생성한다.
그리고 stdafx.h 를 인클루드 해 준다.
6. 새로운 클래스에 메세지 맵을 정의한다.
BEGIN_MSG_MAP(CMyWindow)
END_MSG_MAP()

7. WM_DESTROY 메세지를 위한 핸들러를 추가하고 아래오 같이 코드를 넣어준다.
LRESULT OnDestroy(UINT uMsg, WPARARM wParam, LPRARAM lParam, BOOL& bHandled)
{
PostQuitMessage(0);
return 0;
}

8. 유사하게 WM_PAINT 핸들러를 추가하고 코드를 추가한다.
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bhandled)
{
PAINTSTRUCT ps;
HDC hDC = GetDC();
BeginPaint(&ps);
TextOut(hDC,0,0,_T("HelloWorld"),11);
EndPaint(&ps);
return 0;
}

----WinMain
9. WinMain 의 위와 아라에 CComModule::Init과 Term을 넣는다.
_Module.Init(NULL,hINstance);
//....
_Module.Term();
10. Init 과 Term 사이에 윈도우 클래스의 인스턴스를 정의하고 Create을 호출해서 초기화한다.
CMyWindow wnd;
wnd.Create(NULL, CWindow::rcDefault, _T("Hello"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

11. 이제 빌드하고 실행해보라.
단순히 클라이언트 영역에 "Hello World" 메세지를 볼 수 있을것이다.

Recipe 2: ATL Frame-View App

이 프로젝트에서는 ATL 윈도우 클래스 들로 MFC SDI Frame_view 패러다임 모델을 생성 할
것이다. 뷰와 메뉴와 다이얼로그를 넣어볼 것이다.
1. Recipe 1 과 같이 프로젝트를 생성한다.
---Mainframe Window
2. CWindowImpl<CMainFrame,CWindow,CFrameWinTraits>로 부터 상속받은
CMainFrame을 생성한다. WNDCLASS 구조체 이름을 정의하고 메세지 맵을 추가한다.
DECLARE_WND_CLASS(_T("MyFrame"))

BEGION_MSG_MAP(CMainFrame)
END_MSG_MAP()
3. OnFinalMessage를 상속받아야 한다.
- 몇개 안되는 ATL 가상함수로 WM_NCDESTROY 메세지를 받았을 때 호출된다.
void OnFinalMessage(HWND /*hwnd*/)
{
::PostMessage(0);
}

4. 이제 WinMain 에 몇몇 코드를 넣는다. 위와 아래에 CComModule의 초기화와 종결화를 넣는다.
_Module.Init(NULL, hInstance, NULL);
_Module.Term();

5. mainframe를 include 하고 Init과 Term 사이에 프레임의 인스턴스를 정의하고
Create를 호출해서 초기화한다.
CMainFrame mf;
mf.Create(GetDesktop(),CWindow::reDefault, _T("My App"),0,0,0);
mf.ShowWindow(SW_SHOWNORMAL);
MSG msg;
while(GetMessage(&msg,0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

6. Bake and Serve
-- View Window
7. 이제 view 클래스를 넣어보자.
CWindowImpl<CViewWin,CWindow,CWinTraits>에서 상속 받는 CViewWin을 생성하자.
8. stdafx.h를 인클루드하고 WNDCLASS와 메세지 맵을 정의한다.
CMainFrame 에 맴버변수로 CViewWin의 인스턴스를 추가하고
frame의 WM_CREATE에 view 를 생성하라.
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_wndView.Create(m_hWnd, CWindow::rcDefault, _T("MyView"),0,0,0);
return 0;
}

9. 또한 WM_SIZE에서 view의 사이즈를 구현해 준다.
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT r;
GetClient(&r);
m_wndView.SetWindowPos(NULL,&r,SWP_NOZDRDER | SWP_NOACTIVATE);
return 0;
}


User Interface

이제 WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP 메세지를 핸들링 할것이다

10. 먼저, 두개의 POINT 데이터를 맴버로 두고, 생성자에서 -1 로 초기화한다.
시작 부터 끝날 때까지 각각의 라인을 그리는 트랙을 유지해야한다.
m_stratPoint.x = m_startPoint.y = -1;
m_endPoint.x = m_endPoint.y = -1;

11. 다음으로 OnLButtonDown,에서 라인의 시작점을 저장한다.
LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_startPoint.x = LOWORD(lParam);
m_startPoint.y = HIWORD(lParam);
return 0;
}
12. OnLButtonUP 에서 시작 점을 초기화한다.
LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam,

LPRARAM lParam, BOOL& bHandled)
{
m_startPoint.x = m_startPoint.y = -1;
return 0;
}

13. OnMouseMove 에서는 좀더 많은 작업을 해줘야 한다.

먼저 끝점을 저장한다. 그리고 DC를 얻어 MoveToEX 와 LineTo 를
사용해서 라인을 그린다. 마지막으로 끝점을 시작점에 저장한다.
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_endPoint.x = LOWORD(lParam);
m_endPoint.y = HIWORD(lParam);
HDC hdc = GetDC();
if(m_startPoint.x != -1)
{
MoveToEx(hdc, m_strarPoint.x, m_startPoint.y , NULL);
LineTo(hdc, m_endPoint.x, m_endPoint.y);
m_startPoint.x = m_endPoint.x;
m_startPoint.y = m_endPoint.y;
}
}

Recipe 3: ATL Menus

펜 컬러를 넣는 단순한 메뉴를 넣어보자
1. 프로젝트를 계속 이어가서, 첫번째로, 뷰 클래스에 COLORREF 형의 m_color 맴버 변수를
넣는다. 초기화는 생성자에서 블랙으로 한다.
그리고 이것을 사용해서 OnMouseMove핸들러에서 펜을 만들고 DC 에서 선택한다.
DC를 사용후에는 원래 펜을 선택한다.
HPEN hp = CreatePen(PS_SOLID, 2, m_color);
HPEN op = (HPEN)SelectObject(hdc, hp);


2. 메뉴에 리소스를 삽입한다. 상위 레벨에 Color 이라는 캡션을 추가하고 메뉴 아이템으로
red, green, blue 를 추가한다.
3. WinMain 에서 리소르 헤더를 인클루드하고 mainframe 을 생성하기 전에 메뉴를 로드하고
Create 를 호출한다.
HMENU hMenu = LoadMenu(_Module.GetResourceInstance(),
MAKEiNTERESOURDE(IDR_MENU1));
mf.Create(GetDesktopWindow(), CWindow::rcDefault, _T("My App"), 0,0, (UINT) hMenu);

4. 메뉴 아이템을 위해 command 핸들러를 main frame 에 넣어준다.
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SIZE, OnSize)
COMMAND_ID_HANDLER(ID_COLOR_RED,OnColorRed)
COMMAND_ID_HANDLER(ID_COLOR_GREEN, OnColorGreen)
COMMAND_ID_HANDLER(ID_COLOR_BLUE, OnColorBlue)
END_MSG_MAP()

5. 3개의 핸들러에 대해서 해야할 작업을 적어준다.
LRESULT OnColorRed(WORD wNotifyCode, WORD wID, HWND hWndCtrl, BOOL& bHandled)
{
m_wndView.m_color = RGB(255,0,0);
return 0;
}


Recipe 4: ATL Dialogs

단순한 다이얼로그 리소스를 추가해 보자.

1. 위의 프로젝트에 이어서 "View" 메뉴를 넣고 "Dialog"메뉴 아이템을 넣자.
command 핸들러를 main - frame 메세지 맵에 넣는다.
COMMAND_ID_HANDLER(ID_VIEW_DIALOG, OnViewDialog)
2. 여기서는 CSimpleDialog 를 직접 사용할 것이다.
먼저 새로운 다이얼로그 리소스를 넣고, static box 를 넣는다.
메뉴에 대한 명령 핸들러에서 이것을 이용한다.
LRESULT OnViewDialog(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
CSimpleDialog<IDD_DIALOG1> dlg;
dlg.DoModal();
return 0;
}

3. 좀더 멋을 부리고 싶다면 CSimpleDialog 로 부터 상속 받을 수 있다.
그래서 첫번째로 리소스 에디터에서 combobox 를 넣는다.

    4. 그리고 CListDialog 라는 CSimpleDialog<IDD_DIAOG1>로 부터 상속 받은 새로운 클래스를
    만든다. stdafx.h 를
    인클루드 하는 것을 잊지말라. 새로운 클래스에 메세지 맵을 추가하고 맵에
    베이스 클래스의 메세지 맵에 묶기 위한 코드를 기입한다.
    BEGIN_MSG_MAP(CListDialog)
    CHAIN_MSG_MAP(CSimpleDialog<IDD_DIALOG1>)
    END_MSG_MAP()

    5. 다음으로 combobox에 문자열을 추가하기 위해 WM_INITDIALOG에 코드를 추가해
    줄 것이다. 먼저 콤보박스에 sort 속성을 제거한다.
    LRESULT OnIntiDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
    CWindow combo(GetDlgItem(IDC_COMBO1));
    combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Red");
    combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Green");
    combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Blue");
    return CSimpleDialog<IDD_DIALOG1>::OnInitDailog(uMsg,wParam,lParam,bHandled);
    }
    6. Note : CHAIN_MSG_MAP 매크로 는 맵의 마지막 진입점이다.
    7. DDX/DDV는 어떻게 하는가?
    COMMAND_ID_HANDLER(IDOK, OnOK)
    8. OnOK는 아래와 같이 적어준다. m_text라는 CComBSTR 형 맴버변수에 텍스는 저장할 것이다.
    LRESULT OnOK(WORD, WORD nID, HWND, bool&)
    {
    CComBSTR text;
    GetDlgItemText(IDC_COMBO1,m_text.m_str);
    ::EndDilog(m_hWnd, wID);
    return 0 ;
    }
    9. 마지막으로 다이얼로그로 부터 추출한 텍스트를 사용하기 위해 메뉴 아이템 핸들러를
    업데이트 한다.
    LRESULT OnViewDialog(WORD wnotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
    CListDialog dlg;
    if(IDOK == dlg.DoModal())
    {
    if(dlg.m_text == CComBSTR("Red"))
    m_wndView.m_Color = RGB(255,0,0);
    else if(dlg.m_text == CComBSTR("Green"))
    m_wndView.m_color = RGB(255,0,0);
    else if(dlg.m_text == CComBSTR("Blue"))
    m_wndView.m_color = RGB(0,0,255);
    }
    return 0;
    }

    만약 툴바와 상태바로 이 app 를 확장하고 싶다면 ATL CStatusBarCtrl 과 CToolBarCtrl 클래스를
    이용할 수 있다. 이것은 atlcontrols.h 에정의 되어 있지만 MS가 기본적으로 지원하지 않는다.

    'Computer Science' 카테고리의 다른 글

    [ VMWare ] Workstation / GSX Server / ESX Server 의 차이점  (0) 2009.05.16
    Using the ATL Windowing Classes  (0) 2009.04.28
    wtl  (0) 2009.04.25
    WTL code  (0) 2009.04.25
    COM Architecture : Threading models  (0) 2009.04.24

    + Recent posts