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 阅读(1063)
评论(0) 编辑 收藏 引用 所属分类:
ATL