코딩하는 나귀

저자 : hanburn

날짜 : 2007.08.29

환경 : WTL7.5, VS-2003

 

 

지난시간까지 정리를 하면(오랜만에 강좌를 쓰니까 기억이 않나서~ ) 위저드를 통해서 생성되는 코드의 뼈대를 살펴보고, 메시지맵에 대해서 알아 보았습니다. 그럼 다음단계로는 윈도우의 각종 컨트롤들을 사용하는 방법입니다. 컨트롤의 사용법이라고 해서 윈도우의 공용컨트롤들의 각각의 사용법을 알아보는 것이 아니라 MFC와 비교해서 컨트롤들의 일반적인 사용법을 애기하는 것입니다. 그럼 또 편한 말로 계속하겠습니다.

 

 

WTL에서 제공하는 컨트롤의 종류는 아래와 같습니다.

 

User controls: CStatic, CButton, CListBox, CComboBox, CEdit, CScrollBar, CDragListBox

Common controls: CImageList, CListViewCtrl (CListCtrl in MFC), CTreeViewCtrl (CTreeCtrl in MFC), CHeaderCtrl, CToolBarCtrl, CStatusBarCtrl, CTabCtrl, CToolTipCtrl, CTrackBarCtrl (CSliderCtrl in MFC), CUpDownCtrl (CSpinButtonCtrl in MFC), CProgressBarCtrl, CHotKeyCtrl, CAnimateCtrl, CRichEditCtrl, CReBarCtrl, CComboBoxEx, CDateTimePickerCtrl, CMonthCalendarCtrl, CIPAddressCtrl

Common control wrappers not in MFC: CPagerCtrl, CFlatScrollBar, CLinkCtrl (clickable hyperlink, available in XP and later)

 

이중에서 User / Common 그룹의 컨트롤들은 MFC에서 일상적으로 사용하던 것들이고 추가로 CPagerCtrl, CFlatScrollBar, CLinkCtrl등의 유용한 클래스들도 제공이 되고 있다. 윈도우 공용컨트롤들이야 윈도우에서 제공되는 것들을 MFC에서 랩핑한 방식 비슷하게 WTL도 랩핑하여 제공하고 있고 함수이름도 똑같이 제공한다. 다만, 파라미터의 순서가 조금 다른것도 있다.

 

윈도우 공용 컨트롤들을 사용하는 것은 리소스편집기에서 Dialog에 추가하거나 직접 코드에서 생성하여 사용하는 방법이 있는데, 이 방법에 대해서 자세하게 알아보자.

 

먼저 리소스에서 추가한 컨트롤을 제어 하려면 아래처럼 하면된다.

 

HWND hWndListBox = GetDlgItem(IDC_LIST1);

CListBox list1(hWndListBox);

CListBox list2;

list2.Attach(hWndListBox);

 

CListBox list3 = GetDlgItem(IDC_LIST1);

 

 

GetDlgItem으로 HWND를 얻어와서 생성자에 넣어 주거나 Attach 함수를 이용해서 hwnd를 붙여주면 된다. 또는 간편하게 GetDlgItem의 반환값을 직접 대입하는 식으로 써주면 된다. 그런뒤에는 CListBox의 멤버함수를 통해서 원하는 작업을 하면 된다. 여기서 한가지 알고 넘어갈 것은 list1,list2,list3 같은 래퍼클래스의(여기서는 CListBox) 인스턴스가 소멸된다고 해서 실제적인 CListBox가 파괴되는 것은 아니라는 점이다. 따라서 attach한 핸들을 detach할 필요가 없다.

 

코드에서 직접생성하는 경우는 더 쉽다. 아래는 CButton을 직접 생성하는 경우인데 m_btnTitle을 가지고 해당 버튼을 제어하면 된다.

 

m_btnTitle.Create(m_hWnd, NULL, NULL, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, IDC_NES_WIDGET_BTN_TITLE);

 

컨트롤을 생성해서 제어를 하였으면 이제는 컨트롤에서 발생하는 이벤트를 처리해야 되는데, 이벤트 처리는 지난시간에 살펴본 메시지 핸들러를 통해서 제어하게 된다. 차일드 컨트롤에서 발생되는 이벤트는 부모위도로 WM_COMMAND, WM_NOTIFY 등의 메시지로 전달이 되므로 이 메시지를 잡아서 처리하면 된다. 간단하게 CListCtrl에서 발생하는 몇가지 이벤트처리 예를 보자

 

BEGIN_MSG_MAP_EX(CMainDlg)

        NOTIFY_HANDLER_EX(IDC_LIST, NM_DBLCLK, OnListDblClicked)

NOTIFY_HANDLER_EX(IDC_LIST, LVN_ITEMCHANGED, OnListItemchanged)

END_MSG_MAP()

 

LRESULT        OnListItemchanged(NMHDR* phdr);

LRESULT        OnListDblClicked(LPNMHDR pnmh);

 

 

위에서는 NM 계열의 일반적인 통지메시지와 ListCtrl의 통지메시지인 LVN_ITEMCHANGED를 처리하는 핸들러 함수의 모습이다. (.. 너무 쉽군 ^^)

 

다음으로는 컨트롤 관련해서 DDX라는 방법을 MFC에서 제공하기때문에 WTL에서도 DDX를 유사하게 제공하고 있다. 필자는 DDX/DDV 방법을 별로 사용하지 않지만 그래도 간단하게 알아보고 넘어가도록 하자.

 

DDX를 사용하기 위해서는 WTL의 설계 철학대로 CWinDataExchange 템플릿 클래스에서 상속을 받아야 한다. 그리고 메시지맵 비슷한 DDX맵을 추가해주어야 한다. DDX맵은 CWinDataExchange DoDataExchange 라는 함수를 재정의(override)하는 역할을 한다. 쉽게말하면 상속을 받았으면 꼭 추가를 해주어야 하는 매크로이다.(매크로대신 직접 코드를 써도 되긴 하지만.. ) 메시지맵에 익숙한 우리에게 DDX맵도 익숙할 수밖에 없다. DDX맵을 직접 보고 이야기 하도록 하자.

 

BEGIN_DDX_MAP(CMainDlg)

        DDX_CONTROL(IDC_EDIT, m_wndEdit)

        DDX_TEXT(IDC_EDIT, m_sEditContents)

DDX_FLOAT(IDC_EDIT, m_fEditFloat)

        DDX_FLOAT(IDC_EDIT, m_dEditDouble)

        DDX_CHECK(IDC_SHOW_MSG, m_bShowMsg)

        DDX_CONTROL(IDC_TREE, m_wndTree)

END_DDX_MAP()

 

시작과 끝은 DDX_MAP을 나타내고 중간에 있는 여러가지 DDX_로 시작하는 매크로들에 대해서 알아보자. 첫번째인 DDX_CONTROL은 컨트롤아이디에 해당하는 컨트롤과 변수를 연결시켜주는 역할을 한다. 두번째인 DDX_TEXT는 컨트롤과 변수를 연결시켜주는데, 여기서 변수는 CString, BSTR, CComBSTR 등이나 정적으로 할당된 char 배열도 가능하다. DDX_INT는 컨트롤과 정수 변수를 연결시켜주고, DDX_FLOAT는 컨트롤과 실수를 연결시켜준다. DDX_CHECK는 체크박스컨트롤과 intbool형 변수와 연결이 된고, DDX_RADIO는 라이오버튼과 int형 변수와 연결을 시켜준다.

 

이렇게 DDX_ 관련 매크로를 사용하여 컨트롤과 변수를 연결시킨 다음에는, 필요한 시점에 DoDataExchange() 함수를 호출해주면 MFC에서 UpdateData를 호출하는 것처럼 호출시 파라미터를 TRUE로 하면 컨트롤의 데이터가 변수로, 파라미터를 FALSE로 하면 변수의 값이 컨트롤로 전달이 된다.  그리고 여러가지 DDX_ 매크로 중에서 특정 컨트롤의 변수만 update하고 싶을때는 DoDataExchange의 두번째 인자로 컨트롤 아이디를 넣어주면 된다.

 

DoDataExchange(false, IDC_EDIT);

 

한가지 주의할 점은 DDX_ 매크로중에 잘못되 부분이 있다면 잘못된 부분의 아래에 있는 매크로는 작동이 안 되는 점이다.

 

여기까지 왔으면 다이얼로그 기반의 윈도우를 만들고 거기에 각종 컨트롤을 생성하고, 컨트롤을 제어하고, 컨트롤에서 발생하는 이벤트를 처리할수 있게 되었다. 이정도만 하면 왠만한 프로그램은 그냥 만들 수가 있을 것이다. ^^

 

 

프로그램을 하다보면 스킨등 입히거나 컨트롤의 기능을 확장하기위해서 subclassing을 해야 뭔가 그럴듯해 보이는 프로그램이 나오게 됩니다. 그래서 다음시간에는 Subclassing에 대해서 알아보도록 하겠습니다.