自定义 GUI 系统






2.11/5 (7投票s)
2005年10月12日
3分钟阅读

69760

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包中“轮子是如何转动的”(您可以在这里找到此包这里 - 请随意下载并尝试使用它)。请参见下一个教程,我尝试解释如何实现对对话框、自定义和子类化控件的支持。