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

自定义 GUI 系统

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.11/5 (7投票s)

2005年10月12日

3分钟阅读

viewsIcon

69760

downloadIcon

655

如果您需要对代码进行最大程度的控制,如果您喜欢强制Windows按照您的方式显示,并且如果您足够懒惰,那么本教程正是为您准备的。

动机

如果您不喜欢那些自动生成大量无用代码的工具,您大多不会理解它,因为作者没有费心去编写文档。如果您需要对代码进行最大程度的控制,如果您喜欢强制Windows按照您的方式显示,并且如果您足够懒惰,那么本教程正是为您准备的。

嗯,几个月前,正是这些原因让我想到了一种简单、可扩展且易于使用的GUI系统的终极解决方案。实际上,我喜欢MFC背后的理念,例如:每个窗口类型都有一个单独的类,消息处理程序代替了巨大的窗口过程,但是我希望将它们与纯Win32 API的“低级”特性结合起来。

描述

当您查看源代码时,您会注意到基本框架由两个类组成。(注意:这两个类在源文件中都有或多或少的详细文档。它们是从一个更大的项目中提取出来的,为了本教程的目的,它们被精简到呈现其背后基本原理所需的必要元素。)

第一个类是winbase,它代表一个泛型窗口(就像MFC中的CWnd一样)。其核心功能依赖于标准创建方法create()、创建前和创建后初始化方法以及消息处理程序。消息处理程序实现为特定于消息的处理程序,一个处理程序对应一种类型的消息,除了泛型消息处理程序on_message(),它提供了一种扩展系统功能而不重新编译整个API的方法。泛型处理程序还可以捕获未处理的消息。此类中的所有函数都是virtual

第二个类是winpool,它是所有应用程序窗口的占位符,也充当应用程序管理器。此类只有静态方法,因为每个应用程序不需要此类的多个实例。

现在是“魔法”——传递给CreateWindowEx()的公共窗口过程,在create()中被调用。

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

   //special case: at the moment we don't have 
   //the hwnd associated with our window object
   if(msg == WM_CREATE)
   {
      //find the window we are creating - this window has WDS_INITIAL flag set
      wb = winpool::get_created_window();

      //init windows`s hwnd and data members that need to be obtained 
      //through this hwnd (hdc,...) and set window`s display state to WDS_OPEN
      wb->post_create_window(hwnd);

      //bail with calling the user defined (or default) on_create() handler
      return wb->on_create(wp, lp);
   }

   //standard case: we already have the hwnd assigned 
   //to our window object, window has WDS_OPEN flag set
   //get the window this message belongs to
   wb = winpool::get_window_from_hwnd(hwnd);

   //call message specific handlers
   switch(msg)
   {
   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);
   //call generic handlers
   default:
      if(wb)
      //we have the pointer to the window
         return wb->on_message(msg, wp, lp);
      else
      //for the sake of security when we for any reason
      // don't have the pointer to the window
         return DefWindowProc(hwnd, msg, wp, lp);
   }
}

使用系统

系统已创建,它应该做一些有用的事情。在本教程中,我们将创建一个简单的窗口,它将对鼠标点击和按键做出反应。因此,我们创建了一个泛型窗口的子类,称为MyWindow。此窗口将自身居中显示在屏幕上,并将加载并显示十字准星光标。然后我们覆盖处理程序,以便当用户左键单击窗口时,弹出消息框询问用户是否要关闭窗口。当用户右键单击窗口时,弹出消息框显示单击的坐标,当用户敲击按键时,弹出消息框显示所敲击按键的ASCII值。运行应用程序就轻而易举了。

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");

   //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 */}

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

   return 1;
}

最后的话

如您所见,我们最终得到了一个易于使用且可以进一步扩展的运行GUI系统。现在您应该了解Win32 Pro包中“轮子是如何转动的”(您可以在这里找到此包这里 - 请随意下载并尝试使用它)。请参见下一个教程,我尝试解释如何实现对对话框、自定义和子类化控件的支持。

© . All rights reserved.