65.9K
CodeProject 正在变化。 阅读更多。
Home

WTL 为 WinMain 的 CodeMax 语法高亮编辑器控件提供包装器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2001 年 5 月 18 日

CPOL

6分钟阅读

viewsIcon

141591

downloadIcon

1480

一篇关于 CodeMax 编辑控件的文章。

Sample Image

引言

当我开始写这篇文章时,我只打算说明如何使用我创建的 CodeMax 语法编辑控件包装器。但我很快发现,我还需要解释我在演示应用程序本身所做的事情。所以,我没有简单地发布一套包装器和一个演示程序,而是决定写一篇关于 CodeMax 包装器的小文章,同时扩展一些我在编写使用 Windows Template Library (WTL) 的 MDI 应用程序时发现的东西。

首先,我来告诉你一些关于示例代码布局的信息。如果你曾经使用过 WTL 向导,你就会知道它会将应用程序的所有代码都塞进一系列头文件中。虽然我喜欢将我的组件放在一个头文件中以便于集成,但我讨厌处理以这种方式设置的应用程序代码。所以,我做的第一件事就是将代码分割成各自的源文件和头文件。这样可以实现更干净的实现,而且在我看来,更容易阅读和编辑。

不幸的是,这样做,我破坏了使用 Visual C++ 提供的“添加 Windows 消息处理程序”向导的可能性。但我也选择使用 WTL 实现的“扩展”消息映射(BEGIN_MSG_MAP_EX 等),这与上述向导不兼容。所以,最终它无论如何都无用了。

CodeMax 包装器

我包含的包装器封装了 CodeMax 提供的几乎所有功能。我说几乎,因为我还没有实现一种中心化的方式来更新 CodeMax 控件的设置(当你运行示例时,你会明白我的意思)。

所有类都定义在 cmaxwtl.h 文件中,并且所有类都声明在 cmax 命名空间内。

  • CodeMaxLibrary - 包装了所有基本的 CodeMax 库调用,包括注册和注销。
  • CodeMaxControl - 包装了 CodeMax 控件本身。
  • CodeMaxControlNotifications - 基类,允许你处理来自 CodeMax 控件的通知消息(无论是否已反射)。
  • CodeMaxControlCommands - 基类,处理标准的 CodeMax 命令(剪切、复制、粘贴等)。
  • UndoBlock - 简单的撤销助手,允许你将一系列可撤销的操作分组为一个。

如何使用包装器

首先,你需要声明一个 CodeMaxLibrary 对象实例并对其进行初始化。

// ...

CodeMaxLibrary cmaxlib;

if ( !cmaxlib.Initialize () ) {

    ATLTRACE ( _T ( "CodeMax initialization failed!\n" ) );
    
    return 0; // bail...

}     

// ...

虽然在哪里执行此操作并不重要,但最好尽早执行,以避免以后出现任何麻烦。我建议你在 WinMain 函数或向导为你声明的 Run 函数中执行此操作。

接下来,你需要修改你的“视图”或客户端窗口,并从中派生 CodeMaxControl 类。

请注意,你需要在类声明中使用 DECLARE_WND_SUPERCLASS()。这允许你对窗口进行超级类化,从而创建一个基于 CodeMax 控件的新窗口,并带有一个你稍后将定义的新的窗口过程。

class CCodeMaxWTLSampleView : 
      public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >
{  
   // ...
   
public:

   // Superclass the CodeMax control   

   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
   // ...

}

我还包含了 CodeMaxControlNotifications 类来允许你处理 CodeMax 控件的通知消息。这个类可以用作视图或主框架的基类。在示例中,我在视图中使用它,但可能有一些充分的理由将其用于应用程序的主框架。如果你确实决定在应用程序的主框架中使用它,请务必将所有子窗口的消息转发给主框架。对于 MDI 应用程序,在 MDI 子窗口的消息映射中调用 FORWARD_NOTIFICATIONS() 即可完成此操作。

我们的新视图,带有通知处理,看起来会是这样的

class CCodeMaxWTLSampleView : public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >,
                              public CodeMaxControlNotifications < CCodeMaxWTLSampleView >
{
   // ...
   
public:

   // Superclass the CodeMax control   

   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
  // ...

};

当然,你仍然需要从父窗口将通知消息反射回视图,并在视图的消息映射中链接通知类的消息映射。

Reflection(反射)

在 MFC 中,我们不必真正担心反射消息。只需要子类化一个窗口或控件,它的所有通知消息都会被反射回来。在 WTL 中,情况并非如此,你实际上必须从父窗口将消息反射回发送者。幸运的是,这并不难,因为 ATL 为我们提供了一种快速简单的方法。你只需要记住在父窗口的消息映射中添加 REFLECT_NOTIFICATIONS()。这个宏的优点是它允许你在定义它的类中处理窗口的所有内容。

以下代码片段演示了如何将消息反射回发送它们的子窗口

class CChildFrame : public CMDIChildWindowImpl < CChildFrame >
{ 
  
   // ...
   
   BEGIN_MSG_MAP_EX ( CChildFrame )
   
      // ...
      //
      // Notification handlers
      //      

      // Reflect all the WM_NOTIFY messages to the client window

      REFLECT_NOTIFICATIONS ()
      
   END_MSG_MAP ()
   
   // ...
  
};

消息更改

ATL 和 WTL 为以窗口为中心编程提供的最强大的概念之一是消息更改。这允许你将已经灵活的消息处理方案划分为更小、更易于管理的块。这使我能够提供 CodeMaxControlCommands,一个处理所有 Windows 标准命令消息的类。为剪切和粘贴、撤销/重做等命令提供了基本处理。

要利用这个类的优势,你可以从中派生你的视图,或者像通知处理程序类一样,派生主框架窗口。正如你可能已经猜到的,这还不够。你还必须确保命令消息从主框架传递到视图。请记住,如果你使用的是 MDI 界面,消息必须先经过子框架才能到达视图。

以下宏将使这成为可能(两者都定义在 atlframe.h 中)

  • CHAIN_MDI_CHILD_COMMANDS()
  • CHAIN_CLIENT_COMMANDS()

第一个宏放在主框架的消息映射中,它只是将控制权传递给活动的 MDI 子窗口。第二个宏从主框架接管控制权,并将其传递给子窗口的客户端控件(即视图)。

主框架窗口

class CMainFrame : public CMDIFrameWindowImpl < CMainFrame >, 
                   public CUpdateUI < CMainFrame >,
                   public CMessageFilter, 
                   public CIdleHandler
{ 
   
   // ...
   
   BEGIN_MSG_MAP_EX ( CMainFrame )
   
      // ...
      //
      // Chained Message maps
      //        

      // Pass all unhandled WM_COMMAND messages to the active child window
      CHAIN_MDI_CHILD_COMMANDS ()
         // other chains...
     
   END_MSG_MAP ()
   
   // ...
  
};

子框架窗口

class CChildFrame : public CMDIChildWindowImpl < CChildFrame >
{ 
   // ...
  
   BEGIN_MSG_MAP_EX ( CChildFrame )
   
      // ...
      //
      // Chained Message maps
      //
      
      // Pass all unhandled WM_COMMAND messages to the client window or 'view'

      CHAIN_CLIENT_COMMANDS ()
         // other chains...
      
      // ...
   
   END_MSG_MAP ()
   
   // ...
   
};

我们现在可以自由地在视图中处理任何或所有命令消息。

我们视图的最终版本看起来会是这样的

class CCodeMaxWTLSampleView : 
      public CWindowImpl < CCodeMaxWTLSampleView, CodeMaxControl >,
      public CodeMaxControlNotifications < CCodeMaxWTLSampleView >,
      public CodeMaxControlCommands < CCodeMaxWTLSampleView > 
{  
   // ...

public:

   // Superclass the CodeMax control   
   DECLARE_WND_SUPERCLASS ( NULL, CodeMaxControl::GetWndClassName () )
   
   // View's message map
   BEGIN_MSG_MAP_EX ( CCodeMaxWTLSampleView )    
   
      // ...
      //
      // Chained Message maps
      //

      // Make sure that the notification
      // and default message handlers get a crack at the messages

      CHAIN_MSG_MAP_ALT ( CodeMaxControlNotifications < CCodeMaxWTLSampleView >, 
           CMAX_REFLECTED_NOTIFY_CODE_HANDLERS )
      CHAIN_MSG_MAP_ALT ( CodeMaxControlCommands < CCodeMaxWTLSampleView >, 
           CMAX_BASIC_COMMAND_ID_HANDLERS )
      
   END_MSG_MAP ()    
        
   // ...
}

结束

好了,这次就到这里了(原谅我这个双关语);如果我遗漏了什么,请告诉我。对于那些还在使用 MFC 的人,我也有完整的 MFC 版本包装器……如果你想要,就给我发封邮件;如果需求足够高,我会发布的。

需求

  1. 一种松散耦合的机制,用于从包装器类外部更新 CodeMax 控件。
  2. 一种从磁盘加载和修改自定义语言的方法。(我已经包含了我尝试这样做的开端,但请注意,它打破了太多规则,简直难以置信。我确信有更优雅的方法可以做到这一点。)
© . All rights reserved.