用ATL编写窗口程序

XiangDong 2001-08-23 11:25:08
使用ATL 窗口类

本文由Andrew Whitechapel投稿。
前言
ATL 窗口类并不难学,但是许多开发者认为既然有强大的MFC支持,为什么还要同时学习ATL 窗口开发?这是因为MFC强迫你使用应用程序框架结构,同时存在代码过大,性能低下的问题,你还必须审查基于文档和文档模块的连载机制的持续有效性。使用ATL 窗口类的另一个大好处当然就是易于整合COM支持。如果你希望应用程序更快,更小,并且在选择应用程序结构和连续性协议方面更有弹性,你就不能不试试ATL。
在本文中,我将介绍ATL窗口类,并给出一个ATL框架视图程序的简单向导。你会发现实际上很容易实现与MFC同样的前端功能。由于ATL窗口类与MFC相比之下要小的多,所以ATL窗口类的学习曲线会更短,弯路更少。
尽管ATL主要是设计来支持COM,但它还是包括了一些窗口模型类,你可以用这些类建立有窗口的COM对象(如ActivX控件)和那些不需要包括COM的Windows应用程序。以下列出了最重要的ATL窗口类
Cwindow 一个对用于维护窗口的Win32API函数的简单包装,包括一个窗口句柄和HWND运算用于把一个CWindow对象转换为一个HWND,因此你可以把一个Cwindow对象传送给任何需要一个窗口句柄的函数。
CWindowImpl 你可以使用CWindowImpl建立一个基于新窗口类的窗口,是一个存在类的超类或者是一个存在窗口的子类。
CContainedWindow 实现一个窗口类,该窗口发送消息给其他类的消息映射,允许你在一个类中集中进行消息处理。
CAxWindow 允许你实现一个寄生了一个ActiveX控件的窗口,可以建立一个控件或连接一个已存在的控件。
CDialogImpl 作为基类以实现一个模式的或者非模式对话框。CDialogImpl提供一个对话框处理过程发送消息给派生类的缺省消息映射,不支持ActiveX控件。
CSimpleDialog 实现了一个给定了对话框资源ID的简单的模式对话框。有一个预定义的消息映射处理已知的命令如IDOK和IDCANCEL。
CAxDialogImpl 和CDialogImpl一样作为一个基类实现一个模式的或者非模式的对话框,并提供一个对话框处理过程发送消息到派生类的缺省的消息映射。但支持ActiveX控件,ATL对象向导支持添加一个CAxDialogImpl派生的类到你的工程中并产生一个相应的对话框资源。
CWndClassInfo 管理一个新的窗口类的信息,实质上是WNDCLASSEX的封装。
CWinTraits and CWinTraitsOR 封装了ATL窗口对象的特性(WS_Windows sytle)。
消息映射
一个阻挠人们花时间学习ATL Windows的原因是一种认为ATL消息映射很怪异的观点,不错,它们确实与MFC消息映射不同,但是当你第一次看见那些宏的时候是否就能理解MFC消息映射?事实上,ATL映射令人惊奇的容易掌握。为了可以在CWindowImpl派生类中处理窗口消息,ATL从抽象类CMessageMap中继承。CMessageMap声明了一个纯虚函数ProcessWindowMessasge,它通过BEGIN_MSG_AMP和END_MESSAGE_MAP宏在CwindowImpl派生类中实现。
除了熟悉的MFC格式的消息处理,ATL消息处理函数还接受一个额外的BOOL&类型的参数,这个参数指示该消息是否已经被处理,通常为缺省值TURE。消息处理函数可以设定该参数为FALSE以表示它已经处理了该消息。在这种情况下,ATL可以继续查找一个在消息映射中更深层的消息处理函数。通过设定该参数为FALSE,你可以先作一些处理响应一个消息,然后可以允许缺省的消息过程或者其他处理函数来完成处理该消息。
这里有三组消息映射宏,如下表所列:
· 对所有消息进行处理
· 对WM_COMMAND消息进行命令处理。
· 对WM_NOTIFY消息进行通告处理。
MESSAGE_HANDLER 映射一个窗口消息到一个处理函数。
COMMAND_HANDLER 映射一个WM_COMMAND消息到一个基于通告代码和菜单项ID、控件、或者是加速键的处理函数。
COMMAND_ID_HANDLER 映射一个WM_COMMAND消息一个给基于菜单项、控件、或者加速键的处理函数。
COMMAND_CODE_HANDLER 映射一个WM_COMMAND消息道义个基于通告代码的处理函数。
NOTIFY_HANDLER 映射一个WM_NOTIFY消息到一个基于通告代码和控件ID的处理函数
NOTIFY_ID_HANDLER 映射一个WM_NOTIFY消息到一个基于控件ID的处理函数。
NOTIFY_CODE_HANDLER 映射一个WM_NOTIFY消息到一个基于通告代码的处理函数。
例如,如果你有一个有子控件的ATL对话框类, 你可能有如下一个消息映射:
BEGIN_MSG_MAP(CMyDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_CODE_HANDLER(EN_ERRSPACE, OnErrEdits)
NOTIFY_HANDLER(IDC_LIST1, NM_CLICK, OnClickList1)
NOTIFY_ID_HANDLER(IDC_LIST2, OnSomethingList2)
NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDblClkLists)
END_MSG_MAP()
MFC结构允许它使用两种不同的消息发送方案:沿着层次向上发送窗口消息,或者通过文档-视类发送命令消息。第一个方案在ATL中不太合适,因为它所部分实现的模版类层次结构太松散。第二个方案更不可能,因为ATL没有严格的实现任何等同于MFC文档-视图机构的东西。
ATL提供两种方式处理在单个消息映射中的不同的窗口发出的消息:修改消息映射和消息映射链。一个父窗口也可以处理子控件发送的消息,然后可以作为一个反射消息发回。
可选消息映射
可选消息映射主要设计来与ATL类CContainedWindow共用。该类可以发送它所有的消息到其他类的消息映射。这可以用于实现发送到子窗口的消息被父窗口处理。
CContainedWindow构造器需要给出包括将要使用的消息映射的类的地址、该消息映射中的可选消息映射的ID(对缺省的消息映射为0)。
例如,当你建立一个基于一个Windows控件的ATL控件,对象向导将为该控件产生一个类,含一个CContainedWindow对象来表示该子控件。结果就是,内含的窗口成为ActiveX控件所基于的窗口控件的超类:
class ATL_NO_VTABLE CMyButton :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyButton, &CLSID_MyButton>,
public CComControl<CMyButton>,
//...
{
public:
CContainedWindow m_ctlButton;
CMyButton() : m_ctlButton(_T("Button"), this, 1) { }

BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
END_MSG_MAP()
//...
注意 Button是WNDCLASS类型,不是名称。包含类的指针作为第二个参数传送,数值1送给CcontainedWindow构造函数来标记可选消息映射。
如果你希望为控件处理WM_LBUTTONDOWN消息,你可以按如下方法修改消息映射。在这种情况下消息将会被发送到父窗口的消息映射,然后发送到消息映射的可选部分:
BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()
可选消息映射是一个允许你在一个BEGIN_MSG_MAP/ENDMSG_MAP宏对中加强消息处理的简单策略。
链接消息映射
链接消息映射发送消息到另一个类或对象的消息映射,ATL支持一些映射链宏:
CHAIN_MSG_MAP(theBaseClass) 发送消息到基类的缺省消息映射。
CHAIN_MSG_MAP_ALT(theBaseClass, mapID) 发送消息到基类的可选消息映射。
CHAIN_MSG_MAP_MEMBER(theMember) 发送消息到指定数据成员(从CmessageMap派生)的缺省消息映射。
CHAIN_MSG_MAP_ALT_MEMBER(theMember, mapID) 发送消息到指定数据成员的可选消息映射。
例如,当你建立一个基于Windows控件的ATL控件时,对象向导会产生如下代码:
BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
END_MSG_MAP()
这指定WM_CREAT和WM_SETFOCUS消息将在该类中处理,但是任何其他消息将会被发送到CComControl<>基类的消息映射,同时,如果WM_CREATE或WM_SETFOCUS消息的处理设置bHandled为FALSE,这些消息会送到基类作进一步的处理
发送消息到一个数据成员,你可以作如下修改:
BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
CHAIN_MSG_MAP_MEMBER(m_ctlButton)
END_MSG_MAP()
这假定m_ctlButton是容器窗口的一个数据成员,是一个从CContainedWindow派生的类的实例,你感兴趣的消息在消息映射中有一个入口。
class CMyButtonControl : public CContainedWindow
{
//...
BEGIN_MSG_MAP(CMyButtonControl)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()
因此,链接消息映射允许你从一个映射到另一个链式发送消息――与MFC所采用的方案概念相似。
反射消息
一个父窗口可以反射从子窗口发送的消息,即将消息发回子窗口,同时原消息上加上一个标记。当控件获得这些消息,他可以确定它们是从容器反射回的并正确的处理它们,例如,一个子控件可能需要处理WM_DRAWITEM消息,这时必须在父窗口的消息映射中加上REFLECT_NOTIFICATIONS:
BEGIN_MSG_MAP(CMyDialog)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(IDOK, OnOk)
NOTIFY_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
REFLECT_NOTIFICATIONS宏扩展成调用CwindowImpl::ReflectNotificaions,定义如下:
template <class TBase>
LRESULT CWindowImplRoot<TBase>::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
函数从wParam和lParam中获得发送消息的子控件的窗口句柄,然后以如下方式发送消息:
::SendMessage(hWndChild, OCM_ _BASE + uMsg, wParam, lParam);
子窗口用标准的MESSAGE_HANDLER宏处理反射消息,预定义的反射消息ID在olectrl.h
中定义:
BEGIN_MSG_MAP(CMyContainedControl)
MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
例子中的OCM_DRAWITEM定义如下:
#define OCM_ _BASE (WM_USER+0x1c00)
#define OCM_COMMAND (OCM_ _BASE + WM_COMMAND)
//...
#define OCM_DRAWITEM (OCM_ _BASE + WM_DRAWITEM)
DEFAULT_INFLECTION_HANDLER宏转换消息到原始消息并送入DefWindowProc。
秘诀 1: ATL Window App
这是一个简单练习,证明使用ATL窗口类建立一个简单应用程序是多么的简单:

ATL COM应用程序向导是设计来为COM对象提供一个宿主,如果你要建立一个非COM应用程序,ATL COM应用程序向导代码对你不合适,所以要建立一个ATL应用程序,你有两个选择:
· 建立一个ATL COM应用程序向导的EXE服务器,接受冗余的代码。
· 建立一个Win32应用程序,手动增加ATL支持。
为了能更清楚那些必须的,我们故意不使用任何向导生成的代码,按照第二个方法为我们的应用程序构建最小化的轻量记得框架。
1. 建立一个新的Win32应用程序,选择简单选项,这样我门获得了stdafx.h和stdafx.cpp(‘afx’是从MFC残留的,但是名字没有关系,重要的是预编译头文件(PCH))
ATL 支持
2. 修改stdafx.h增加必要的ATL头文件和一个全局CComModule对象的外部引用。
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <atlwin.h>
3. 增加一个ATL对象映射到主CPP文件,它是空的,我们在这里声明CcomModual对象。
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()
4. 增加一个和工程名称相同的IDL文件,它必须有一个库块,它不需要作为工程的一部分建立(你可以设置Project/Settings把它从建立中排除),但是为了使用向导它必须存在,你可以使用任何你喜欢的库名称。
library SomethingOrOther
{
};
Window
5. 没有任何建立指定的ATL Window类的向导支持,所以你只能使用新建对话框增加一个普通类,然后手动修改它。在类视图中右击根节点选择新类,使用Generic Class类类型,名称CmyWindow,基类CwindowImpl<CmyWindow>,你会获得一个关于ATL窗口类的警告,因为向导会为你的新类产生一个新的头文件但是不会包括stdafx.h(因为已经包括了atlwin.h),修改的方法是包括stdafx.h。
6. 在你的新窗口类中,声明一个消息映射然后保存。
BEGIN_MSG_MAP(CMyWindow)
END_MSG_MAP()
7. 在类视图中,右击你的新窗口类增加一个WM_DESTORY消息的处理以发出一个退出消息:
LRESULT OnDestroy(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
PostQuitMessage(0);
return 0;
}
8. 同样的处理WM_PAINT,编码打印一个字符串。由于没有ATL类包装HDC(在WTL中有)我们直接使用API代码:
LRESULT OnPaint(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
PAINTSTRUCT ps;
HDC hDC = GetDC();
BeginPaint(&ps);
TextOut(hDC, 0, 0, _T("Hello world"), 11);
EndPaint(&ps);
return 0;
}
WinMain
9. 在WinMain的开始和结尾分别调用CcomModule::Init和Term
_Module.Init(NULL, hInstance);
// ...
_Module.Term();
10. 在Init和Term中间,声明一个你的Window类对象,初始化并调用Create函数,并设置一个消息循环:
CMyWindow wnd;
wnd.Create(NULL, CWindow::rcDefault, _T("Hello"),
WS_OVERLAPPEDWINDOW|WS_VISIBLE);

MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
11. 现在,建立和运行你的程序,你会发现一个象上面那样在客户区写者“Hello World”的窗口显示。
很容易是吗?现在让我们继续看下一个例子……
秘诀2: ATL 框架视图程序
在这个工程中我们模仿MFC SDI框架视图为范例建立了一个应用程序,但是使用了ATL窗口类。本程序的第一个版本看起来和前一个简单程序一样,但是我们会以后加上视图,菜单和对话框。
1. 建立一个新的‘Simple’Win32应用程序,象以前一样修改stdafx.h增加需要的ATL头文件和对CcomModule对象的外部引用,增加一个ATL对象映射到主CPP文件并声明全局CcomModule对象,增加一个包括库块的框架IDL文件。
Mainframe Window
2. 右击类视图的根节点,新建Generic类,名为CmainFrame,基类CWindowImpl<CmainFrame,Cwindow,CframeWinTraits>,注意CframeWinTraits是在atlwin.h中定义的特别适用于主框架窗口。在新的CmainFrame类中,声明WNDCLASS结构名和消息映射。
DECLARE_WND_CLASS(_T("MyFrame"))
BEGIN_MSG_MAP(CMainFrame)
END_MSG_MAP()
3. 我们已经继承了函数OnFinalMessage——ATL中极少的虚函数之一——,当收到WM_NCDESTROY消息时会被ATL调用,你必须重载它以发送退出消息。
void OnFinalMessage(HWND /*hWnd*/)
{
::PostQuitMessage(0);
}
4. 现在在WinMain中增加一些代码。在开始和结尾处调用CcomModule初始化/中止例程。
_Module.Init(NULL, hInstance, NULL);

_Module.Term();
5. #include 主框架类头文件,在Init和Term之间声明一个框架实例,声明另一个框架实例,调用Create初始化,然后运行一个消息循环。
CMainFrame mf;
mf.Create(GetDesktopWindow(), CWindow::rcDefault,
_T("My App"), 0, 0, 0);
mf.ShowWindow(SW_SHOWNORMAL);

MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
6. 试试看
View Window
7. 现在我们将加入视图。右击类视图建立另一个新的类,同样使用普通类,名称为CviewWin,从CwindowImpl<CviewWin, Cwindow, CwinTraits>,注意这里没有为视图预定义特别的CwinTraits。
8. 像前面一样包括stdafx.h并声明WNDCLASS和消息映射,在CmainFrame类中加入一个CviewWin实例作为成员,在框架中处理WM_CREAT消息,在其中建立视图。
LRESULT OnCreate(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_wndView.Create(m_hWnd, CWindow::rcDefault,
_T("MyView"), 0, 0, 0);
return 0;
}
9. 在框架中处理WM_SIZE消息以设定视图尺寸,再试一下。
LRESULT OnSize(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
RECT r;
GetClientRect(&r);

m_wndView.SetWindowPos(NULL, &r,
SWP_NOZORDER | SWP_NOACTIVATE );

return 0;
}
用户界面
现在,我们处理WM_LBUTTONDOWN,WM_MOUSEMOVE和WM_LBUTTONUP消息来提供一个简单版本的图写程序,允许用户用鼠标画线。他很清晰的表现了用户界面反映和消息处理。

10. 首先,增加两个POINT数据成员到视图类,在构造函数中初始化它们为-1,-1。我们需要保持跟踪所画每一条线的开始和结束点,-1,-1是鼠标不可能到达的坐标点值。
m_startPoint.x = m_startPoint.y = -1;
m_endPoint.x = m_endPoint.y = -1;
11. 下一步,添加对三个鼠标消息的处理,与MFC CWnd类的鼠标消息处理不同,ATL版没有声明lParam作为一个Cpoint(甚至连POINT也不是),所以你必须自己从中取出鼠标坐标。首先OnLButtonDown代码记录了鼠标坐标作为线的开始点。
LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_startPoint.x = LOWORD(lParam);
m_startPoint.y = HIWORD(lParam);

return 0;
}
12. 在OnLButtonUp,重置开始点为-1:
LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_startPoint.x = m_startPoint.y = -1;
return 0;
}
13. OnMouseMove需要作更多的工作,首先,设置线的终点为输入的鼠标坐标,然后获得DC,用MoveToEx和LineTo函数画线,最后,用终点坐标更新开始点坐标。这样下一条线将从现在的终点开始。
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
m_endPoint.x = LOWORD(lParam);
m_endPoint.y = HIWORD(lParam);

HDC hdc = GetDC();
if (m_startPoint.x != -1 )
{
MoveToEx(hdc, m_startPoint.x, m_startPoint.y, NULL);
LineTo(hdc, m_endPoint.x, m_endPoint.y);
m_startPoint.x = m_endPoint.x;
m_startPoint.y = m_endPoint.y;
}

return 0;
}
14. 编译运行,你可能会问为什么在ATL重没有任何消息Crackers?似的,这就是WTL所要做的事情之一…
秘诀3: ATL 菜单
继续上一个框架视图工程,我们将加上一个简单的菜单让用户选择钢笔颜色。

1. 继续这个工程,首先加入一个公共的COLORREF成员变量到视图类,叫做m_color,在构造函数重初始化为黑色。在OnMouseMove处理函数重建立一个笔,并选入DC,完成之后如下所示选择原来的黑色到DC中。
HPEN hp = CreatePen(PS_SOLID, 2, m_color);
HPEN op = (HPEN)SelectObject(hdc, hp);
2. 插入菜单资源,增加一个顶层菜单名:Color和三个子菜单red,greed,blue。
3. 在WinMain中,#inlcude资源头文件,在建立主框架之前读入菜单,并将菜单句柄传送给Create调用。
HMENU hMenu = LoadMenu(_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDR_MENU1));
mf.Create(GetDesktopWindow(), CWindow::rcDefault,
_T("My App"), 0, 0, (UINT)hMenu);
4. 我们现在将在主框架中处理菜单命令,包括资源头文件并手动更新消息映射
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SIZE, OnSize)
COMMAND_ID_HANDLER(ID_COLOR_RED, OnColorRed)
COMMAND_ID_HANDLER(ID_COLOR_GREEN, OnColorGreen)
COMMAND_ID_HANDLER(ID_COLOR_BLUE, OnColorBlue)
END_MSG_MAP()
5. 使三个处理函数作一些显而易见的工作,并重新测试:
LRESULT OnColorRed(WORD wNotifyCode, WORD wID,
HWND hWndCtl, BOOL& bHandled)
{
m_wndView.m_color = RGB(255,0,0);
return 0;
}
秘诀 4: ATL 对话框
我们现在加入一个简单的对话框资源,对子控件(Cedit,CcomboBox和其他)的丰富支持是MFC的特点之一,ATL却没有这些——尽管WTL有。那么这又有多难?我们的对话框将有一个Combobox,为了示意如何在对话框中编程操作控件,我们将故意不在资源编辑器中把字符串加入Combo。
1. 继续以上工程,加入一个新的顶级菜单“View”和一个菜单项“Dialog”到菜单中,在主框架中处理该菜单项命令消息:
COMMAND_ID_HANDLER(ID_VIEW_DIALOG, OnViewDialog)
2. 现在增加对话框,在第一个版本中,我们直接使用CsimpleDialog,首先插入一个新的对话框资源,加入一个新的静态框,然后更改菜单命令处理使用该对话框,重建并测试它。
LRESULT OnViewDialog(WORD wNotifyCode, WORD wID,
HWND hWndCtl, BOOL& bHandled)
{
CSimpleDialog<IDD_DIALOG1> dlg;
dlg.DoModal();

return 0;
}
3. 如果我们在对话框中需要更复杂的功能,我们必须从CsimpleDialog中派生,首先在资源编辑器中为对话框加上一个下拉框。

4. 建立一个新的类ClistDialog,从CSimpleDialog<IDD_DIALOG1>中派生。别忘了包括stdafx.h,为新的类增加一个消息映射,并在消息映射中增加一个入口,连接到基类的消息映射中。
BEGIN_MSG_MAP(CListDialog)
CHAIN_MSG_MAP(CSimpleDialog<IDD_DIALOG1>)
END_MSG_MAP()
5. 下一步,我们编辑WM_INITDIALOG以增加一些字符串到Combobox,首先,去掉Combobox的排序属性,右击ClistDialog类选择增加Window消息处理,更改过滤器到Dialog,增加和编辑WM_INITDIALOG的消息处理函数,如下所示,你会发现Combobox类对象的定义与MFC中的非常相似。
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{
CWindow combo(GetDlgItem(IDC_COMBO1));
combo.SendMessage(CB_ADDSTRING, 0, (LPARAM)"Red");
combo.SendMessage(CB_ADDSTRING, 0, (LPARAM)"Green");
combo.SendMessage(CB_ADDSTRING, 0, (LPARAM)"Blue");

return CSimpleDialog<IDD_DIALOG1>::OnInitDialog(
uMsg, wParam, lParam, bHandled);
}
6. 注意,确信CHAIN_MSGMAP宏是该映射中的最后一个入口,更改主框架的菜单项处理函数使用新的ClistDialog类,编译测试。
7. 好了,但是DDX/DDV又怎么办?让我们编辑IDOK按钮的代码以从列表中获得选定的字符串,首先在消息映射中加入相应的宏:
COMMAND_ID_HANDLER(IDOK, OnOK)
8. 下一步修改OnOk代码如下,我们将把文本存入我们对话框的一个成员CComBSTR对象m_text:
LRESULT OnOK(WORD, WORD wID, HWND, BOOL&)
{
CComBSTR text;
GetDlgItemText(IDC_COMBO1, m_text.m_str);
::EndDialog(m_hWnd, wID);

return 0;
}
9. 最后,更新框架类菜单项处理函数,使用对话框类的文本,编译并测试。
LRESULT OnViewDialog(WORD wNotifyCode, WORD wID,
HWND hWndCtl, BOOL& bHandled)
{
// CSimpleDialog<IDD_DIALOG1> dlg;
CListDialog dlg;
if (IDOK == dlg.DoModal())
{
if (dlg.m_text == CComBSTR("Red"))
m_wndView.m_color = RGB(255,0,0);
else if (dlg.m_text == CComBSTR("Green"))
m_wndView.m_color = RGB(0,255,0);
else if (dlg.m_text == CComBSTR("Blue"))
m_wndView.m_color = RGB(0,0,255);
}

return 0;
}
如果你希望为你的程序扩充工具条和状态条,你可以使用ATL CstatusBarCtrl和CtoolBarCtrl类,这些在atlcontrols.h中定义,尽管微软没有官方支持他们,在下一篇文章中,我将考虑使用WTL——另一个非官方支持的为人类库,你可以在WTL和ATL的前端支持之间做一些比较,就可以知道ATL/WTL与MFC的关系。

注:我在阅读和试用过程中,发现不使用COM支持时不加入IDL文件和对象映射也没有关系。
...全文
503 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
bighead 2001-09-05
  • 打赏
  • 举报
回复
wuliao
乱七八糟 2001-09-05
  • 打赏
  • 举报
回复
snowwindli@sohu.com
lpt 2001-09-05
  • 打赏
  • 举报
回复
up
Jeffreywong 2001-09-05
  • 打赏
  • 举报
回复
jeffreywong@sina.com

thx.
xcex 2001-09-05
  • 打赏
  • 举报
回复
xcex@263.net

:)
码农猿圈圈 2001-09-05
  • 打赏
  • 举报
回复
呵呵,"木头"也会发牢骚?!
我来看看...
XiangDong 2001-09-05
  • 打赏
  • 举报
回复
o
XiangDong 2001-08-23
  • 打赏
  • 举报
回复
好像没多少人感兴趣
vcmfc 2001-08-23
  • 打赏
  • 举报
回复
是不是你翻译的???


不知道你对我这个建议如何?,文章不错。
寻找有共同技术兴趣之友:合办一份从事研究《ATL/COM》的免费电子杂志!
http://www.csdn.net/expert/TopicView.asp?id=252007
XiangDong 2001-08-23
  • 打赏
  • 举报
回复
如果要word文档可以留下email

1,649

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 非技术类
社区管理员
  • 非技术类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧