本文旨在介绍如何使用CodeMax语法编辑控件的封装类,并在Windows Template Library (WTL)环境下开发多文档界面(MDI)应用程序。在开发过程中,发现需要解释演示应用程序本身的内容,因此决定撰写一篇关于CodeMax封装类的文章,并扩展一些在编写MDI应用程序时发现的内容。
如果曾经使用过WTL向导,可能会熟悉它将应用程序的所有代码都塞进一组头文件的做法。尽管喜欢将组件放入单一的头文件中以简化集成,但并不喜欢以这种方式设置应用程序代码。因此,首先将代码分割成相应的源文件和头文件。这样做使得实现更加清晰,并且在看来,它更易于阅读和编辑。
不幸的是,这样做破坏了使用Visual C++提供的“添加Windows消息处理程序”向导的机会。但也选择了使用WTL实现的“扩展”消息映射(BEGIN_MSG_MAP_EX等),这与上述向导也不兼容。因此,最终它无论如何都是无用的。
包含的封装类几乎封装了CodeMax提供的所有内容。说“几乎”,是因为还没有实现一个中心方式来更新CodeMax控件的设置(当运行示例时,会明白意思)。
所有类都在cmaxwtl.h文件中定义,所有类都在cmax命名空间中声明。
首先,需要声明一个CodeMaxLibrary对象的实例并初始化它。
CodeMaxLibrary cmaxlib;
if (!cmaxlib.Initialize()) {
ATLTRACE(_T("CodeMax initialization failed!\n"));
return 0;
}
虽然可以在任何地方执行此操作,但最好尽早进行,以避免后续的麻烦。建议在WinMain函数或向导为声明的Run函数中执行此操作。
接下来,需要修改“视图”或客户端窗口,并从CodeMaxControl类派生它。
class CCodeMaxWTLSampleView : public CWindowImpl {
public:
// Superclass the CodeMax control
DECLARE_WND_SUPERCLASS(NULL, CodeMaxControl::GetWndClassName())
};
还包含了CodeMaxControlNotifications类,允许处理CodeMax控件的通知消息。这个类可以作为视图或主框架的基础。在示例中,将其用于视图,但使用它在应用程序的主框架中可能有一些很好的理由。如果决定在应用程序的主框架中使用它,请确保将所有子窗口的消息转发到主框架。在MDI应用程序的情况下,在MDI子的消息映射中放置一个调用FORWARD_NOTIFICATIONS()可以做到这一点。
在MFC中,不需要真正担心反射消息。一个人所需要做的就是子类化一个窗口或控件,它所有的通知消息就会为反射回来。在WTL中,情况并非如此,实际上必须将消息从父窗口反射回其子窗口。幸运的是,这并不难,因为ATL为提供了一个快速简单的方法来做这件事。所要做的就是记得在父窗口的消息映射中添加REFLECT_NOTIFICATIONS()。这个宏的美妙之处在于,它允许在定义它的类中处理窗口的所有内容。
class CChildFrame : public CMDIChildWindowImpl {
BEGIN_MSG_MAP_EX(CChildFrame)
// Notification handlers
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
};
ATL和WTL为以窗口为中心的编程提供的最强大的概念之一是消息更改。这允许将已经灵活的消息处理方案划分为更小、更易于管理的部分。这使能够提供CodeMaxControlCommands类,它处理所有Windows的标准命令消息。为剪切和粘贴以及撤销/重做等命令提供了基本处理。
为了从这个类中获益,可以从它派生视图,或者像通知处理程序类一样,从主框架窗口派生。正如可能已经猜到的,这并不是所需要做的全部。还必须确保命令消息从主框架传递到视图。请记住,如果使用的是MDI界面,消息必须首先通过子框架才能到达视图。
以下宏将使这成为可能(两者都在atlframe.h中定义):
class CMainFrame : public CMDIFrameWindowImpl, public CUpdateUI, public CMessageFilter, public CIdleHandler {
BEGIN_MSG_MAP_EX(CMainFrame)
// Chained Message maps
CHAIN_MDI_CHILD_COMMANDS()
other chains...
END_MSG_MAP()
};
class CChildFrame : public CMDIChildWindowImpl {
BEGIN_MSG_MAP_EX(CChildFrame)
// Chained Message maps
CHAIN_CLIENT_COMMANDS()
other chains...
END_MSG_MAP()
};
class CCodeMaxWTLSampleView : public CWindowImpl, public CodeMaxControlNotifications, public CodeMaxControlCommands {
public:
// Superclass the CodeMax control
DECLARE_WND_SUPERCLASS(NULL, CodeMaxControl::GetWndClassName())
// View's message map
BEGIN_MSG_MAP_EX(CCodeMaxWTLSampleView)
// Chained Message maps
CHAIN_MSG_MAP_ALT(CodeMaxControlNotifications, CMAX_REFLECTED_NOTIFY_CODE_HANDLERS)
CHAIN_MSG_MAP_ALT(CodeMaxControlCommands, CMAX_BASIC_COMMAND_ID_HANDLERS)
END_MSG_MAP()
};