졸리운_곰 2009. 4. 25. 21:48

http://serious-code.net/moin.cgi/WTL#head-985b205a281fe54ca528258d5d78b84520539515

serious-code.net WTL

  1. Windows Template Library
  2. 시작하기
    1. 설치
    2. 링크
    1. 리스트뷰 컨트롤 Full Row Select 켜기
    2. 모달 다이얼로그 컨트롤 리사이징
    3. ClientEdge 그리기
    4. Contained Window
    5. 더블 버퍼링
    6. IDLE 처리
    7. Custom Modal Dialog
    8. DDX
    9. 메뉴 업데이트
    10. 단축키
    11. RichEdit 컨트롤 사용하기
  3. 다운로드


1 Windows Template Library

    [WWW]http://sourceforge.net/projects/wtl/

    윈도우즈 애플리케이션 및 UI 컴포넌트 개발을 위한 C++ 라이브러리. ATL을 기반으로 개발되었으며, 각종 컨트롤, 대화창, 윈도우 프레임, GDI 오브젝트 등을 제공한다.

    API로 GUI 프로그래밍하려니 귀찮고, MFC를 쓰자니 의존성이 짜증나는 경우, 대안으로 사용할 수 있다. 템플릿 기반으로 작성되었고, 특별한 DLL 같은 것을 필요로 하지 않는다. 문제는 관련 문서가 너무 부족하다는 것이다. 공식적인 헬프 파일도 존재하지 않고, 인터넷에 있는 문서들도 MFC 같은 것에 비하면 거의 없는 것과 마찬가지다.

    장단점을 요약해 보자면 다음과 같다.

    from [WWW]http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=18197

    I built a couple of medium sized GUI apps with WTL.

    My experience was generally positive, but I had been using ATL for at least four years by that point, and considered something of an expert in it. With that background, WTL was a breath of fresh air to me compared to the MFC prison.

    The plus side of WTL:
    • Designed like ATL rather than MFC. Functionality is composed via multiple inheritance in a shallow hierarchy rather than using a deep single-inheritance hierarchy where the functionality you want isn't in the branch you need it (What do you mean I need a view to have scrolling support?)
    • All source code is available, and it's quite readable if you understand ATL. This also alleviates the "end of life" arguments - who cares if it doesn't get maintained? You've got the source code, and it's easy to tweak.
    • Since WTL is built around composition, it's dirt simple to extend to do whatever you need to.
    • Support from WTL's author is quite good - there's a WTL yahoo group that he hangs out on.

    Down sides of WTL:
    • If you don't know ATL, or are afraid of templates, stay away. It won't make any sense at all.
    • Documentation is essentially non-existant. There's a good intro article (2 parts) on http://www.develop.com somewhere that explains the basics of what's in the library and how to use it. http://www.codeproject.com has a WTL section with some good stuff. But there's no books and no printed articles and no likelyhood of any appearing any time soon.
    • Very slow compile times. Lots of templates mean lots of work for the compiler.

    In general, WTL is the expert's tool. If you know Win32 programming well, and understand how ATL is put together, WTL will make you VERY productive, even without the handholding of wizards. If you aren't, well, you're in for a bit of a tough road. But the destination is well worth it.

    On the other hand, if you just want to churn out a couple of dialogs, use WinForms instead.


2 시작하기

3 팁

    3.1 리스트뷰 컨트롤 Full Row Select 켜기

      myListCtrl.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT);
      API를 모르니 원...

    3.2 모달 다이얼로그 컨트롤 리사이징

      class cMainDialog : public CDialogImpl<cMainDialog>,                    public CDialogResize<cMainDialog> // !!!{    ...    BEGIN_MSG_MAP_EX(cMainDialog)        MSG_WM_INITDIALOG(OnInitDialog)        ...        CHAIN_MSG_MAP(CDialogResize<cMainDialog>) // !!!    END_MSG_MAP()    // !!!    BEGIN_DLGRESIZE_MAP(cMainDialog)        DLGRESIZE_CONTROL(IDC_GEOMETRY_GROUP, DLSZ_SIZE_X)        DLGRESIZE_CONTROL(IDC_LEVEL_EDIT, DLSZ_SIZE_X)        DLGRESIZE_CONTROL(IDC_LEVEL_BUTTON, DLSZ_MOVE_X)        DLGRESIZE_CONTROL(IDC_GEOMETRY_EDIT, DLSZ_SIZE_X)        DLGRESIZE_CONTROL(IDC_GEOMETRY_BUTTON, DLSZ_MOVE_X)    END_DLGRESIZE_MAP()    LRESULT OnInitDialog(HWND hwndFocus, LPARAM lParam)    {        ...        DlgResize_Init(true, true, WS_THICKFRAME);  // !!!        ...    }};

    3.3 ClientEdge 그리기

      딱히 ATL/WTL 과 관련된 것도 아니고, 윈도우 생성할 때 WS_EX_CLIENTEDGE 플래그 주면 그만인 내용이지만...
      CRect client, tmp;GetClientRect(&client);CBrush light, face, shadow, black;light.CreateSolidBrush(::GetSysColor(COLOR_BTNHIGHLIGHT));face.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));shadow.CreateSolidBrush(::GetSysColor(COLOR_BTNSHADOW));black.CreateSolidBrush(::GetSysColor(COLOR_3DDKSHADOW));// toptmp.SetRect(client.left, client.top, client.right - 1, client.top + 1);dc.FillRect(tmp, shadow);tmp.SetRect(client.left, client.top + 1, client.right - 1, client.top + 2); dc.FillRect(tmp, black);// bottomtmp.SetRect(client.left, client.bottom - 2, client.right - 1, client.bottom - 1);dc.FillRect(tmp, face);tmp.SetRect(client.left, client.bottom - 1, client.right - 1, client.bottom - 0);dc.FillRect(tmp, light);// lefttmp.SetRect(client.left, client.top + 1, client.left + 1, client.bottom - 1);dc.FillRect(tmp, shadow);tmp.SetRect(client.left + 1, client.top + 1, client.left + 2, client.bottom - 2);dc.FillRect(tmp, black);// righttmp.SetRect(client.right - 2, client.top + 1, client.right - 1, client.bottom - 2);dc.FillRect(tmp, face);//tmp.SetRect(client.right - 2, client.top + 1, client.right - 1, client.bottom - 2);//dc.FillRect(tmp, shadow);// centertmp.SetRect(client.left + 2, client.top + 2, client.right - 2, client.bottom - 2);dc.FillSolidRect(tmp, ::GetSysColor(COLOR_APPWORKSPACE));

    3.4 Contained Window

      class cMainWindow : public cWindowImpl<...>{private:    CContainedWindowT<CEdit> m_Edit;public:    BEGIN_MSG_MAP(cMainWindow)        ...    ALT_MSG_MAP(1)        MESSAGE_HANDLER(WM_CHAR, OnEditChar)    END_MSG_MAP()    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)    {        ...        if (!m_Edit.Create(this, 1, m_hWnd, rcDefault))            return -1;        ...    }    LRESULT OnEditChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)    {        ...    }};
      메시지맵 ID에 주의

    3.5 더블 버퍼링

      CDoubleBufferImpl 클래스를 상속받으면 된다. 그런데 약간 이상한게 CDoubleBufferImpl 클래스 내부에 OnPaint 함수와 메시지맵이 정의가 되어있는데, OnPaint 함수가 제대로 호출되지 않는다. 그러므로 실제 상속받은 클래스에서 한번 선언해준다.
      class cMainDialog : public CDialogImpl<cMainDialog>,                    public CDoubleBufferImpl<cMainDialog>{public:    enum { IDD = IDD_MAIN };    BEGIN_MSG_MAP_EX(cMainDialog)        ...        MESSAGE_HANDLER(WM_PAINT, OnPaint) // 메시지맵은 정의하되, 실제 함수는 선언하지 않아야한다.        ...    END_MSG_MAP()    void DoPaint(CDCHandle dc)    {        // 여기서 뭔가를 실제로 그려준다.        ...    }};

    3.6 IDLE 처리

      CIdleHandler 클래스는 기본적으로 idle 처리를 한번 한 후에는 WM_MOUSEMOVE, WM_PAINT 이외의 메시지가 도착해야만 다시 idle 처리를 한다. 항상 idle 처리를 하게 만들기 위해서는 클래스를 상속받는 것이 편하다.
      class cMainFrame : public CFrameWindowImpl<cMainFrame>,                    public CUpdateUI<cMainFrame>,                   public CMessageFilter,                   public CIdleHandler{    ...    virtual BOOL OnIdle()    {        // 여기서 할 일을 정의한다.        return TRUE; // 별 의미 없다.    }    ...};class cCustomMessageLoop : public CMessageLoop{public:    virtual BOOL OnIdle(int nIdleCount)    {        CMessageLoop::OnIdle(nIdleCount);        // 리턴값이 중요하다! CMessageLoop::OnIdle 함수는 기본적으로 FALSE를 반환한다.        return TRUE;     }};int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,                    LPTSTR lpCmdLine, int nCmdShow){    cCustomMessageLoop theLoop;    cMainFrame theWindow;    theModule.Init(NULL, hInstance);    theModule.AddMessageLoop(&theLoop);        ....        return 0;}

    3.7 Custom Modal Dialog

      CDialogImpl 클래스를 상속받은 모달 Dialog를 만든 경우, Dialog 속성 창에서 Style을 Popup으로 해줘야 정상적으로 동작한다.

    3.8 DDX

      DDX_FLOAT 매크로를 사용하기 위해서는 atlddx.h 파일을 include하기 전에 _ATL_USE_DDX_FLOAT 매크로를 정의해줘야한다.

    3.9 메뉴 업데이트

      메뉴 업데이트(활성화/비활성화, 체크 등)를 사용하기 위해서는 CUpdateUI를 상속받은 다음, BEGIN_UPDATE_UI_MAP, END_UPDATE_UI_MAP 맵을 정의해줘야한다. 실제 업데이트는 UIEnable, UISetCheck 등의 함수를 이용한다.
      class cMainFrame : public CFrameWindowImpl<cMainFrame>,                     public CUpdateUI<cMainFrame>,                    public CMessageFilter,                    public CIdleHandler {     BEGIN_UPDATE_UI_MAP(cMainFrame)         UPDATE_ELEMENT(ID_VIEW_ORIGINALMESH, UPDUI_MENUPOPUP)         UPDATE_ELEMENT(ID_VIEW_NAVIGATIONMESH, UPDUI_MENUPOPUP)         UPDATE_ELEMENT(ID_VIEW_WIREFRAME, UPDUI_MENUPOPUP)         UPDATE_ELEMENT(ID_VIEW_BODY, UPDUI_MENUPOPUP)     END_UPDATE_UI_MAP() }; 

    3.10 단축키

    • 단축키를 지원하기 위해서는 CMessageFilter를 상속받고, PreTranslateMessage 함수를 오버라이드해준 다음, 윈도우 생성 시에 AddMessageFilter 함수를 이용해 메시지 루프에다 필터를 추가해야한다.

    3.11 RichEdit 컨트롤 사용하기

      RichEdit 컨트롤을 사용하기 위해서는 다음과 같은 매크로를 atlctrls.h 파일을 include 하기 전에 정의하고, 어딘가에서 라이브러리를 직접 로드해줘야한다.
      // RichEdit 1.0을 사용하기 위한 정의//#define WINVER                0x0400//#define _WIN32_IE     0x0400//#define _RICHEDIT_VER 0x0100// RichEdit 2.0을 사용하기 위한 정의#define WINVER          0x0500#define _WIN32_WINNT    0x0500#define _WIN32_IE       0x0501#define _RICHEDIT_VER   0x0200#include <atlctrls.h>...HINSTANCE hInstRich = ::LoadLibrary(CRichEditCtrl::GetLibraryName());ATLASSERT(hInstRich != NULL);...::FreeLibrary(hInstRich);



4 다운로드