본문 바로가기
개발언어/C++

Notify Handling in ATL Dialog (Standard Control)

by 엔돌슨 2010. 5. 27.
반응형

Notify Handling in ATL Dialog (Standard Control)
SUMMARY
 
    윈도우즈는 메시지 기반의 시스템이다. 모든 하위 윈도우를 제어할 때에는 해당 윈도우에 고유의 메시지들을 전달하여 처리한다. 하위 윈도우에서 부모 윈도우로 정보를 전달하는 방법도 메시지를 이용하는데, 이 경우에는 특별히 통지 메시지 (Notification Message)라고 부른다.
 
    이 문서에서는 ATL 다이얼로그에서 하위 윈도우(표준 콘트롤)에서 보내는 통지 메시지를 부모 윈도우 (다이얼로그)에서 받아서 처리하는 방법에 대해 설명한다.
 
 
FUNDAMENTALS
 
    MFC와 유사하게 ATL에서도 메시지 맵을 제공하여 편리하게 메시지를 처리할 수 있도록 해준다. ATL에서 제공하는 메시지 맵의 형태는 아래와 같다.
 

BEGIN_MSG_MAP(CStandardNotifyDlg)
    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    COMMAND_ID_HANDLER(IDOK, OnOK)
    COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
    COMMAND_ID_HANDLER(IDC_LIST_COLOR, OnColorNotify)
END_MSG_MAP()

 
    위 메시지 맵에서 COMMAND_ID_HANDLER가 리스트 박스에서 보내는 통지 메시지(WM_COMMAND 메시지)를 처리하기 위한 매크로인데 이와 유사한 형태의 매크로가 많이 제공되므로 각 매크로를 정확히 구분하기 위하여 깔끔하게 정리를 해보자.    
 
        COMMAND_HANDLER(id, code, func)
        NOTIFY_HANDLER(id, code, func)
 
    통지 메시지를 처리하는 매크로 중 가장 기본적인 것이 COMMAND_HANDLER와 NOTIFY_HANDLER이다. 에디트 박스나 리스트 박스 등의 표준 콘트롤은 WM_COMMAND를 이용하여 통지 메시지를 전해주고 공통 콘트롤은 WM_NOTIFY를 이용한다. 위 매크로는 각각 표준 콘트롤과 공통 콘트롤의 통지 메시지를 처리하기 위한 매크로이다.
 
    콘트롤의 실제 사용예는 다음과 같다. id에는 콘트롤의 식별자를 대입하고, code에는 통지 메시지의 종류, func에는 메시지 처리함수를 대입한다.
 
COMMAND_HANDLER(IDC_EDIT1, EN_CHANGE, OnEdit1Change)
NOTIFY_HANDLER(IDC_TOOLBAR1, TBN_BEGINDRAG, OnToolbar1BeginDrag)
 
    위 매크로들은 각 통지 메시지마다 메시지 처리함수를 따로 지정하는 방식인데 특정 콘트롤에서 보내는 모든 통지 메시지를 하나의 함수에서 처리하고 싶다면 다음 매크로를 사용한다. 콘트롤에서 보낸 통지 메시지의 종류는 메시지 처리함수에서 확인하여 알맞게 처리한다. 포함된 샘플 프로그램은 이 매크로를 이용하였다.
 
        COMMAND_ID_HANDLER(id, func)
        NOTIFY_ID_HANDLER(id, func)
 
    위와는 반대로 콘트롤의 id에는 상관없이 특정 통지 메시지를 하나의 함수에서 모두 처리하고 싶다면 다음 매크로를 사용한다. 필요하다면 메시지 처리함수에서 콘트롤 id를 구분해서 처리해주면 된다.
 
        COMMAND_CODE_HANDLER(code, func)
        NOTIFY_CODE_HANDLER(code, func)
 
    특정 범위에 해당하는 콘트롤에 대한 통지 메시지를 한꺼번에 처리하고자 할 때에는 아래 매크로를 사용한다. 앞에서 살펴본 매크로보다 사용빈도가 많지는 않지만 메뉴 항목을 처리할 때에는 편리하게 사용되기도 한다.
 
        COMMAND_RANGE_HANDLER(idFirst, idLast, func)
        NOTIFY_RANGE_HANDLER(idFirst, idLast, func)
 
    통지 메시지 처리와 관련한 자세히 정리는 수십 페이지가 필요하며 여기서 이것을 다룬다는 것은 한마디로 시간낭비다. 문헌 [1]에 자세한 설명이 있으니 참고하기 바란다.
 
 
SAMPLE PROGRAM
 
    샘플 프로그램은 왼쪽의 리스트 박스의 통지 메시지를 받아서 오른쪽 리스트 박스에 통지 메시지의 내용을 표시한다. 아래에 샘플 프로그램 화면을 표시하였다.
 
 
 
 
IMPLEMENTATION
 
    샘플 프로그램에는 두개의 리스트 박스가 있는데 이를 다루기 위해 각각 멤버 변수를 선언하였다. ATL에서 콘트롤을 다룰 때에는 일반적으로 CContainedWindow 클래스를 이용하는데 <List 1>에 이 클래스를 이용하는 두가지 방법을 보여준다.
 
 

<List 1> Control member variable and CContainedWindow class 

    CStandardNotifyDlg() : m_lbColor(this, 1),
                           m_lbLog(this, 2)
    {
    }


 

BEGIN_MSG_MAP(CStandardNotifyDlg)
    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    COMMAND_ID_HANDLER(IDOK, OnOK)
    COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
    COMMAND_ID_HANDLER(IDC_LIST_COLOR, OnColorNotify)
ALT_MSG_MAP(1// Color ListBox
ALT_MSG_MAP(2// Log ListBox

END_MSG_MAP()


 

private:
    CContainedWindow            m_lbColor;
    CContainedWindowT<CListBox> m_lbLog;

 
    CContainedWindowT<CListBox>에서 사용된 CListBox 클래스는 ATL에서 표준으로 제공하는 클래스가 아니다. 이것을 사용하기 위해서는 atlcontrol.h 파일이 따로 필요하며 마이크로 소프트의 샘플 프로그램에 포함되어 있다. 아무튼 이 파일을 포함하고 네임 스페이스까지 지정해주는 것이 필요한데 그 방법은 다음과 같다.

<List 2> Including atlcontrols.h header file

#include "resource.h"       // main symbols
#include "atlcontrols.h"
#include <atlhost.h>
using namespace ATLControls;
 
 
    다이얼로그의 OnInitDialog 함수에서 리스트 박스 콘트롤을 변수와 연결하고 필요한 항목을 리스트 박스에 추가하는 작업을 수행한다. SubclassWindow 함수의 사용 방법을 기억해두자.
 

<List 3> OnInitDialog

    LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        // Attach contained windows to control
        m_lbColor.SubclassWindow(GetDlgItem(IDC_LIST_COLOR));
        m_lbLog.SubclassWindow(GetDlgItem(IDC_LIST_LOG));

        HWND hWnd = GetDlgItem(IDC_LIST_COLOR);
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("White"));
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("Black"));
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("Yellow"));
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("Green"));
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("Red"));
        SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM)_T("Blue"));

        return 1// Let the system set the focus
    }
 
 
    리스트 박스의 통지메시지는 WM_COMMAND 메시지를 이용해서 보내므로 부모 윈도우는 WM_COMMAND 메시지의 핸들러를 이용하여 리스트 박스에서 보낸 통지 메시지를 받는다. 아래 소스에서 사용한 COMMAND_ID_HANDLER 매크로는 IDC_LIST_COLOR 콘트롤에서 보낸 모든 통지 메시지를 OnColorNotify 함수에서 처리하겠다는 것이다.
 

<List 4> COMMAND_ID_HANDLER

BEGIN_MSG_MAP(CStandardNotifyDlg)
    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    COMMAND_ID_HANDLER(IDOK, OnOK)
    COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
    COMMAND_ID_HANDLER(IDC_LIST_COLOR, OnColorNotify)
ALT_MSG_MAP(1// Color ListBox
ALT_MSG_MAP(2// Log ListBox
END_MSG_MAP()
 
 
    아래에 핸들러 함수를 표시하였다. 함수의 마지막 부분에 m_lbLog.SendMessage()와 같은 방식으로 사용한 것은 CContainedWindowT<CListBox>로 변수 선언을 했기 때문에 가능한 것이다. MFC에서 사용하던 것과 비슷하게 사용할 수 있으므로 약간(내 입장에서는 '약간'이다) 더 편리하게 사용할 수 있다.
 

<List 5> Notification Handler Function

 
    LRESULT OnColorNotify(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
        switch (wNotifyCode)
        {
        case LBN_DBLCLK:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_DBLCLK"));
            break;
        case LBN_ERRSPACE:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_ERRSPACE"));
            break;
        case LBN_KILLFOCUS:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_KILLFOCUS"));
            break;
        case LBN_SELCANCEL:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_SELCANCEL"));
            break;
        case LBN_SELCHANGE:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_SELCHANGE"));
            break;
        case LBN_SETFOCUS:
            SendMessage(m_lbLog, LB_ADDSTRING, 0, (LPARAM)_T("LBN_SETFOCUS"));
            break;
        }

        long nCurSel = SendMessage(m_lbLog, LB_GETCOUNT, 0, 0);
        if (nCurSel != LB_ERR)
        {
            m_lbLog.SendMessage(LB_SETCURSEL, nCurSel-1, 0);
            //SendMessage(m_lbLog, LB_SETCURSEL, nCurSel-1, 0);
        }

        return 0;
    }
 
REFERENCE
 
[1] Brent Rector, Chris Sells, "ATL Internals", Addison-Wesley, 1999, pp.435~452.
[2] Project folder : D:\KDSONG\Study\Visual C++\atl com activex\Dialog\NotifyHandling\