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

自定义 GUI 系统 - 第二部分:对话框和控件

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.77/5 (10投票s)

2005年10月17日

4分钟阅读

viewsIcon

69004

downloadIcon

585

本文解释了自定义 GUI 系统中对话框和各种控件的支持是如何实现的。

前言

在我之前的教程中,我尝试解释了一个自定义 GUI 系统的基本框架布局,并假设您已经熟悉了我介绍的解决方案。如果您不知道我在说什么,或者觉得需要回顾一下,请点击链接:自定义 GUI 系统 - 第一部分:基本框架。(注意:与本教程的第一部分一样,所有类都在源文件中都有或多或少详细的文档。它们是从一个更大的项目中提取出来的,并且为了本教程的目的,它们已被简化为展示其基本原理所需的实质性元素。)

对话框

让我们从今天主题的比较容易的部分开始:windialog 类。它继承自 winbase 类,并且是该类在对话框语言中的直接翻译。主要区别在于标准的创建方法 create() 被重写,使其不做任何事情。它只是返回 false。原因是 windialog 类有自己的创建方法 create_dialog()create_modal()。第一个创建无模式对话框,第二个创建有模式对话框。这两种方法都接受三个参数:应用程序实例句柄、父窗口句柄以及对话框的资源 ID。所有初始化都在这些方法中进行,因此只需调用它们即可创建一个对话框对象,并使用资源编辑器中设计的模板打开对话框窗口(在我看来,视觉资源编辑器,即使是 VC++ 中的那个,也是非常有用的并且生成代码相对干净的工具)。接下来是代码中显示的 dialog_proc()window_proc() 之间的区别(两种过程相同的代码已被省略 - 请参考下面“控件”部分更新的 window_proc()

//different procedure type
BOOL CALLBACK dialog_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
   winbase* wb = 0;

   //we process WM_INITDIALOG instead of WM_CREATE
   if(msg == WM_INITDIALOG)
   {
      //exactly the same code as in window_proc()
   }

   //get the window this message belongs to
   wb = winpool::get_window_from_hwnd(hwnd);

   //call message specific handlers
   switch(msg)
   {
   //exactly the same code as in window_proc()
   // .
   // .
   // .
   //up to call to generic handler
   default:
      if(wb)
      //we have the pointer to the window
         return wb->on_message(msg, wp, lp);
   }
   //and for unprocessed messages
   return false;
}

控件

本节可以从简要查看更新的 window_proc() 开始。您可能还记得,这是在本教程第一部分为我们的框架窗口注册的过程。它也被自定义控件使用。正如您将看到的,过程中有新的处理程序调用。这些处理程序(在 winbase 类中定义)允许我们分别处理相应的消息(而不是通过 on_message() 处理程序)。

LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
   winbase* wb = 0;

   if(msg == WM_CREATE)
   {
      wb = winpool::get_created_window();
      wb->post_create_window(hwnd);
      return wb->on_create(wp, lp);
   }

   wb = winpool::get_window_from_hwnd(hwnd);

   switch(msg)
   {

   //////////////////////////////////////////////
   //NEW - added new handlers to support custom controls
   case WM_COMMAND:
      return wb->on_command(wp, lp);
   case WM_PAINT:
      return wb->on_paint(wp, lp);
   case WM_NCPAINT:
      return wb->on_ncpaint(wp, lp);
   case WM_MOUSEMOVE:
      return wb->on_mousemove(wp, lp);
   case WM_LBUTTONDOWN:
      return wb->on_lbuttondown(wp, lp);
   case WM_KILLFOCUS:
      return wb->on_killfocus(wp, lp);
   case WM_SETFOCUS:
      return wb->on_setfocus(wp, lp);
   //////////////////////////////////////////////

   case WM_LBUTTONUP:
      return wb->on_lbuttonup(wp, lp);
   case WM_RBUTTONUP:
      return wb->on_rbuttonup(wp, lp);
   case WM_CLOSE:
      return wb->on_close(wp, lp);
   case WM_DESTROY:
      return wb->on_destroy(wp, lp);
   case WM_NCDESTROY:
      return wb->on_ncdestroy(wp, lp);
   default:
      if(wb)
         return wb->on_message(msg, wp, lp);
      else
         return DefWindowProc(hwnd, msg, wp, lp);
   }
}

下一步,让我们声明 winctrl 类,它是自定义控件的基类。该类继承自 winbase 类,并实现了对自定义控件状态的访问,支持控件外观的操作,支持消息路由,以及我认为最重要的部分,即 attach() 方法,该方法允许将类对象附加到资源编辑器中创建的任何对话框控件。最后,可以介绍一个用于子类化控件的基类,即 wintruectrl 类。该类继承自 winctrl 类,因此支持自定义控件的所有方面。然而,子类化控件有一个重要的区别:它们没有 window_proc(),而是有自己的过程 subcls_proc(),该过程不调用 on_create(),因为这些控件在处理完 WM_CREATE 后被子类化。子类过程 subcls_proc() 会为所有未处理的消息调用 on_message() 处理程序。此处理程序的默认实现会调用子类控件的原始窗口过程。另请注意,wintruectrl 类中所有处理程序的默认实现都必须调用子类控件的原始窗口过程。为了实现这一点,winbase 类包含用于子类化/取消子类化窗口的新方法,以及一个用于调用子类窗口原始窗口过程的方法。

使用系统

本教程中,我们将创建一个

  1. 一个包含单个选择列表框(将被子类化)和两个按钮(将是自定义的)的测试对话框模板。
  2. 一个继承自 winctrl 类的 MyButton 类,它将创建一个可点击的平面按钮。
  3. 一个继承自 wintruectrl 类的 MyListbox 类,它将子类化通用列表框类,并允许我们在列表末尾追加字符串。
  4. 一个继承自 windialogMyDialog 类,它将封装我们的测试对话框模板。

我们还将使用第一部分教程中的通用窗口的子类,称为 MyWindow。该窗口将自己居中显示在屏幕上,还将加载并显示一个十字准星光标。我们将重新实现 on_rbuttonup() 处理程序,以便它打开我们的测试对话框。最后,我们添加两行代码来注册/取消注册我们的自定义 MyButton 窗口类。

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
{
   //initialize application manager
   winpool::open_session(hinst);

   //register our window class
   winpool::register_class(hinst, "MyWindow");

   ///////////////////////////////////////////////////
   //NEW - registering MyButton class 
   //could be put into winpool::open_session()
   winpool::register_class(hinst, "MyButton");
   ///////////////////////////////////////////////////

   //window instance
   MyWindow wnd;

   //position and dimensions array (dims={x, y, w, h})
   //we don`t need to specify position since the window centers itself
   int dims[] = {0, 0, 640, 480};

   //initialize window data members needed for creating the window
   //(e.g hinstance, classname and display state to WDS_INITIAL)
   wnd.pre_create_window(hinst, "MyWindow");

   //create the window - we specify only the owner 
   //(NULL=desktop), caption and dimensions
   wnd.create(NULL, "Tutorial 1 - Use mouse buttons and keyboard keys", dims);

   //finally show the window on the screen
   wnd.show_window(true);

   //run message loop until WM_QUIT is encountered
   while(winpool::dispatch_message()){ /* run */}

   ///////////////////////////////////////////////////
   //NEW - unregistering MyButton class,
   //could be put into winpool::close_session()
   winpool::unregister_class(hinst, "MyButton");
   ///////////////////////////////////////////////////

   //clean up
   winpool::unregister_class(hinst, "MyWindow");
   winpool::close_session();

   return 1;
}

最后的话

恭喜您,您已经完成了!我们已经将我们的小 GUI 系统扩展到类似这样的程度。

winbase           -> MyWindow
|- windialog      -> MyDialog
'- winctrl        -> MyButton
   '- wintruectrl -> MyListbox

现在您应该能够开发自己的窗口类型、控件等。作为灵感,您可以查看 Win32 Pro 包(您可以在 这里 找到该包 - 随意下载并进行实验)。如有疑问,请随时给我发邮件。

© . All rights reserved.