ATL中的對話框類:
現在我們對ATL中的窗口類有了一定的了解,接著我們來學習對話框類。在你的項目中,可能有很多對話框資源,從最簡單的“關于”模式對話框到復雜的滿是控件的非模式對話框。ATL提供了CSimpleDialog類和CDialogImpl類來簡化我們使用對話框資源的過程。
CSimpleDialog
CSimpleDialog是一個從模版創建模式對話框的類。它提供了一些標準按紐(如OK和CANCEL)的處理過程。你可以將CSimpleDialog想象成是一種消息對話框(Message Box),不同的是你可以在對話框編輯器中編輯它的外觀。
要顯示這樣一個對話框,比如當你點擊“幫助”菜單中的“關于”菜單項時顯示關于對話框,你需要在主窗口類中添加如下的消息映射:
BEGIN_MSG_MAP( CMyMainWindow )
COMMAND_ID_HANDLER( ID_HELP_ABOUT, onHelpAbout )
...
LRESULT onHelpAbout( WORD, WORD, HWND, BOOL& )
{
CSimpleDialog<IDD_DIALOG1> dlg;
int ret = dlg.DoModal();
return 0;
}
我們可以看到對話框資源的ID(IDD_DIALOG1)被作為一個模版參數傳遞給CSimpleDialog類,DoModal方法顯示對話框。當用戶點擊OK按鈕時,CSimpleDialog類關閉對話框并返回按鈕的ID。(CSimpleDialog類實現了對按鈕IDOK,IDCANCEL,IDABORT,IDRETRY,IDIGNORE,IDYES和IDNO的響應。)
CDialogImpl
CSimpleDialog類只能夠處理簡單的模式對話框,對于更加復雜的對話框或者模式對話框,就要用到CDialogImpl類。(其實CSimpleDialog是CDialogImpl中的一種特例。)
如果我們需要實現一個非模式對話框,我們必須從CDialogImpl派生出一個新類,并將新類的類名作為模板參數傳遞給CDialogImpl類,就象前面的CWindowImpl一樣:
class CMyModelessDialog: public CDialogImpl
{
和CSimpleDialog不同,我們不需要將對話框資源的ID作為模板參數傳遞給它,但是我們必須將這個類和對話框資源聯系起來,我們通過在類中定義一個枚舉變量實現:
public:
enum { IDD = IDD_DIALOG1 };
然后定義消息映射表:
BEGIN_MSG_MAP( CMyDialog )
MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
MESSAGE_HANDLER( WM_CLOSE, OnClose )
...
END_MSG_MAP()
響應函數的定義和前面的一樣,但是有一點需要注意,如果你實現的是一個非模式對話框,那么在WM_CLOSE消息的響應函數中必須調用DestroyWindow:
LRESULT OnClose( UINT, WPARAM, LPARAM, BOOL& )
{
DestroyWindow();
return 0;
}
...
}; // CMyModelessDialog
要在屏幕上創建這樣一個對話框,需要創建這個類的一個實例并調用Create方法:
CMyModelessDialog dlg;
dlg.Create( wndParent );
如果對話框資源沒有選中WS_VISIBLE屬性,我們需要這樣讓對話框顯示出來:
dlg.ShowWindow( SW_SHOW );
下面的例子有一個非模式的對話框可以接受用戶輸入的字符串,然后在主窗口中顯示這個字符串。對話框中有一個編輯框控件和一個按鈕;當按鈕被點擊時,對話框調用它所屬窗口的DoSomething方法對編輯框中的字符串進行處理,它所屬的窗口是一個超類化的列表框控件,DoSomething方法的功能是將字符串添加到列表框中。
#include "atlbase.h"
CComModule _Module;
#include "atlwin.h"
#include "resource.h"
class CMyWindow: public CWindowImpl<CMyWindow>
{
public:
DECLARE_WND_SUPERCLASS( "MyWindow", "listbox" )
BEGIN_MSG_MAP( CMyWindow )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
END_MSG_MAP()
LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& )
{
PostQuitMessage( 0 );
return 0;
}
void DoSomething( LPCTSTR s )
{
SendMessage( LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(s) );
}
};
class CMyDialog: public CDialogImpl<CMyDialog>
{
public:
enum { IDD = IDD_DIALOG1 };
BEGIN_MSG_MAP( CMyDialog )
COMMAND_ID_HANDLER( IDC_BUTTON1, OnButton )
MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
MESSAGE_HANDLER( WM_CLOSE, OnClose )
END_MSG_MAP()
LRESULT OnButton(WORD, WORD, HWND, BOOL&)
{
char buf[100];
m_ed.GetWindowText( buf, 100 );
m_owner.DoSomething( buf );
return 0;
}
LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& )
{
m_owner.Attach( GetParent() );
CenterWindow( m_owner );
m_ed = GetDlgItem( IDC_EDIT1 );
return TRUE;
}
LRESULT OnClose( UINT, WPARAM, LPARAM, BOOL& )
{
DestroyWindow();
m_owner.Detach();
return 0;
}
CMyWindow m_owner;
CWindow m_ed;
};
CMyDialog dlg;
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
_Module.Init( NULL, hInstance );
CMyWindow win;
win.Create( NULL, CWindow::rcDefault, _T("modeless dialog test"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE );
dlg.Create( win );
dlg.ShowWindow( SW_SHOW );
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) ){
if( !IsWindow(dlg) || !dlg.IsDialogMessage( &msg ) ){
DispatchMessage( &msg );
}
}
_Module.Term();
return 0;
}
指定窗口類的信息:這篇文章的大部分內容都是在講述怎樣處理窗口類的行為——窗口怎樣響應消息。在行為之外,一個窗口類還具有一些其它的重要的屬性,比如樣式、類名、背景顏色和指針等等。這一節介紹怎樣使用ATL中提供的宏來指定這些屬性。
使用Window Traits指定窗口的樣式
到目前為止,所有例子中的窗口樣式都是在調用Create方法時指定的:
CMyWindow wnd;
wnd.Create( NULL, CWindow::rcDefault, _T("Hello"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE );
如果你不指定任何樣式和擴展樣式,ATL將使用默認的樣式;這些默認的樣式是作為窗口的特征定義的,默認特征是CControlWinTraits,定義如下:
typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
|WS_CLIPSIBLINGS, 0> CControlWinTraits;
CWinTraits是一個模板類,它需要2個參數:窗口樣式、擴展窗口樣式。
在CWindowImpl的定義中,CControlWinTraits作為默認的模板參數傳遞給CWindowImpl:
template <class T,
class TBase = CWindow,
class TWinTraits = CControlWinTraits>
class CWindowImpl : public ...
所以在默認情況下,從CWindowImpl派生的窗口都具有可視、子窗口、裁剪兄弟窗口、裁減子窗口的屬性。
我們也能定義自己的窗口特征:
typedef CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,0>
MyTraits;
然后,從CWindowImpl派生一個窗口類,指定自己的窗口特征:
class CMyWindow: public CWindowImpl<CMyWindow,CWindow,MyTraits>
{...};
或者象下面這樣更加直接:
class CMyWindow: public CWindowImpl<
CMyWindow,
CWindow,
CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,0>
>
{...};
注意,我們必須提供全部的三個模板參數:派生類,基類(CWindow)和特征類。
CMyWindow窗口現在具有的默認的樣式為“可見的彈出窗口”,所以我們可以在Create方法中省略樣式參數:
CMyWindow wnd;
wnd.Create( NULL, CWindow::rcDefault, _T("Hello") );
// style: WS_OVERLAPPEDWINDOW|WS_VISIBLE
我們也可以重寫窗口特征:
ovwnd.Create( NULL, CWindow::rcDefault, _T("Hello"),
WS_OVERLAPPEDWINDOW ); // not visible
窗口特征也可以包含擴展樣式:
class CClientWindow: public CWindowImpl<CClientWindow, CWindow,
CWinTraits< WS_OVERLAPPEDWINDOW|WS_VISIBLE, WS_EX_CLIENTEDGE > >
{...};
DECLARE_WND_CLASS
使用DECLARE_WND_CLASS宏可以指定窗口的類名:
DECLARE_WND_CLASS("my window class");
這等價于:
DECLARE_WND_CLASS_EX(
"my window class",
CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, // default style
COLOR_WINDOW // default color
);
DECLARE_WND_CLASS_EX
使用DECLARE_WND_CLASS_EX宏可以指定窗口類名、樣式和背景顏色:
class CMyWindow: public CWindowImpl<CMyWindow>
{
public:
DECLARE_WND_CLASS_EX(
"my window class", // class name
CS_HREDRAW|CS_VREDRAW, // class style
COLOR_WINDOW // background color
);
BEGIN_MSG_MAP(CMyWindow)
...
所謂的窗口類名是指注冊的窗口類的名字,如果我們不指定窗口類名,ATL將自動生成一個,但是當我們使用Spy++之類的工具的時候,你將會發現我們自己取的類名比"ATL:00424bd0"之類的名字要有用得多。
類樣式是按照按位或組合的。
背景顏色必須是標準系統顏色之一。
CWndClassInfo
我們也可以定義超出DECLARE_WND_宏能力之外的窗口類。如果你看看DECLARE_WND_CLASS的定義你就會發現它定義了一個CWndClassInfo結構,并且一個函數返回這種結構類型的值:
#define DECLARE_WND_CLASS(WndClassName) \
static CWndClassInfo& GetWndClassInfo() \
{ \
static CWndClassInfo wc = \
{ \
{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, \
StartWindowProc, \
0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, \
WndClassName, NULL }, \
NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
}; \
return wc; \
}
CWndClassInfo結構提供了更靈活的自定義的可能,它是這樣定義的:
struct CWndClassInfo
{
struct WNDCLASSEX
{
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
HICON hIconSm;
} m_wc;
LPCSTR m_lpszOrigName;
WNDPROC pWndProc;
LPCSTR m_lpszCursorID;
BOOL m_bSystemCursor;
ATOM m_atom;
CHAR m_szAutoName[13];
ATOM Register(WNDPROC* p);
};
例如,要指定一個窗口的指針,我們可以將m_lpszCursorID設置為指針的名字,如果它是一個系統指針,將m_bSystemCursor設置為TRUE,否則設置為FALSE。注意DECLARE_WND_CLASS宏是怎樣將這兩個成員變量分別設置為IDC_ARROW 和 TRUE的。既然DECLARE_WND_宏不能讓我們改寫這些默認的值,我們可以這樣做:
class CMyWindow: public CWindowImpl<CMyWindow>
{
public:
static CWndClassInfo& GetWndClassInfo()
{
// a manual DECLARE_WND_CLASS macro expansion
// modified to specify an application-defined cursor:
static CWndClassInfo wc =
{
{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
StartWindowProc,
0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL,
"MyWindow", NULL },
NULL, NULL, MAKEINTRESOURCE(IDC_CURSOR1), FALSE, 0, _T("")
};
return wc;
}
...
結論:ATL提供了一種簡單的、雅致的并且功能強大的窗口編程模式。在那些方便的封裝好了的函數、消息映射和宏之外,還有一些技術諸如鏈接、窗口的子類化和超類化、被包含的窗口和消息反射等也使得設計和實現窗口和對話框非常靈活。或許ATL給人最深的印象就是:功能強大、靈活性好,但是不會占用太多的內存和系統開銷。
posted on 2007-03-13 10:08
jay 閱讀(1117)
評論(0) 編輯 收藏 引用 所屬分類:
ATL