DuiLib教程--从win32窗口开始
来源:互联网 发布:我的世界0.14.0家具js 编辑:程序博客网 时间:2024/06/03 06:55
win32窗口程序
大家看到这个标题肯定会问“大哥,你是不是搞错了,我是来学DuiLib的,你给我扯什么win32程序”。其实,万变不离其宗,DuiLib也只是一个ui界面库,还是得建立在win32程序的基础上的,还是得从winmain这个狗洞钻进去。如果不搞懂win32的窗口消息机制,DuiLib的消息处理过程肯定得绕晕你。所以我建议大家建立一个空的win32应用程序,添加一个winmain.cpp,把下面的代码手打一遍,每一行的作用都搞懂(不会的windows API 一定要自己去查MSDN
),就算是为学习DuiLib打下了一个良好的基础。
#include <Windows.h>#include <stdio.h>#include <tchar.h>// 窗口过程LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ switch (message) { case WM_LBUTTONDOWN: MessageBox(hWnd, "你单击了鼠标左键", "WM_LBUTTONDOWN", MB_OK); break; case WM_CHAR: char szChar[64]; sprintf_s(szChar, 64, "你按下了键盘键:%c", wParam); MessageBox(hWnd, szChar, "WM_CHAR", MB_OK); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... SetTextColor(hdc, RGB(255,0,0)); SetBkColor(hdc, RGB(0,255,0)); TextOut(hdc, 200, 200, "Hello World!", strlen("Hello World!")); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: break; } return DefWindowProc(hWnd, message, wParam, lParam);}int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ // 注册窗口类 WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_HAND); wcex.hbrBackground = (HBRUSH)(GetStockObject(BLACK_BRUSH)); wcex.lpszMenuName = NULL; wcex.lpszClassName = "mywndclass"; wcex.hIconSm = NULL; RegisterClassEx(&wcex); // 创建窗口 HWND hWnd = CreateWindowEx(0, "mywndclass", "This is a win32 wnd", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, hInstance, NULL); // 显示窗口 ShowWindow(hWnd, nCmdShow); // 窗口消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)){ TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam;}
运行效果图如下:
是不是很丑,怪我咯,我要是做的还看,那公司就不会给我们配个美美的美工了。言归正传,大家可以修改下窗口背景刷、字体背景色和前景色,然后再看看效果,加深理解和印象。
是不是SO easy,只需要记住这条主线,注册窗口类->创建窗口->显示窗口->窗口消息循环,在接下来学习DUiLib的过程中我们就不会被绕晕了。
使用DuiLib的CWindowWnd
上面的win32窗口程序还是面向过程编程,C++是擅长面向对象编程OOP的,我们很自然的想到可以用一个类去封装窗口句柄HWND和对应创建窗口、显示窗口等方法,DuiLib中CWindowWnd就是这个窗口类,分别在UIBase.h和UIBase.cpp中声明和定义。
拿到一个类,首先就是到头文件中去看她提供的方法,一个封装良好的类通过方法名就能大致猜出她的作用。所以在看我的DuiLib教程时一定要打开DuiLib工程find到对应的位置。我只会贴出关键的代码段讲解,其它的你应该到源代码中去自己有个大概了解,有兴趣的甚至可以自己尝试去封装一个窗口类,再来对照看DuiLib中的CWindowWnd类,这样可以锻炼你的设计类能力。
下面先创建一个空win32工程,在winmain.cpp添加如下代码测试下CWindowWnd类:
#include "../../DuiLib/UIlib.h"using namespace DuiLib;#ifdef _DEBUG# ifdef _UNICODE# pragma comment(lib, "../../Lib/DuiLib_ud.lib")# else# pragma comment(lib, "../../Lib/DuiLib_d.lib")# endif#else# ifdef _UNICODE# pragma comment(lib, "../../Lib/DuiLib_u.lib")# else# pragma comment(lib, "../../Lib/DuiLib.lib")# endif#endifclass CFrameWnd : public CWindowWnd{public: virtual LPCTSTR GetWindowClassName() const{ return _T("FrameWnd"); } virtual void OnFinalMessage(HWND hWnd){ delete this; }};int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd ){ // new一个窗口对象 CFrameWnd* pFrame = new CFrameWnd; // 注册窗口类、创建窗口 pFrame->Create(NULL, _T("sample01"), UI_WNDSTYLE_FRAME, UI_WNDSTYLE_EX_FRAME, 100, 100, 800, 600, NULL); // 显示窗口、进入窗口消息循环 pFrame->ShowModal(); return 0;}
使用CFrameWnd继承自CWindowWnd,CWindowWnd必须实现的一个纯虚接口是GetWindowClassName来表明她的窗口类名。在OnFinalMessage中delete this是因为DuiLib中需要使用new来生成一个窗口,delete可以防止内存泄漏(在后面的DuiLib程序中可以看到都只有new而没有delete,这是因为DuiLib内部在窗口销毁时已经做了delete的操作
)。编译生成这段代码,运行后也能顺利创建显示一个窗口,让我们看一下关键的几个方法:
一、创建窗口
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu){ if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL; if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL; m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this); ASSERT(m_hWnd!=NULL); return m_hWnd;}
可以发现在调用CreateWindowEx这个windows API前会先调用RegisterWindowClass注册窗口类。 还有一点需要注意的是 CreateWindowEx的最好一个参数将this指针作为参数传递了进去,这个玩意在后面可有妙用。
二、显示窗口并进入消息循环
UINT CWindowWnd::ShowModal(){ ASSERT(::IsWindow(m_hWnd)); UINT nRet = 0; HWND hWndParent = GetWindowOwner(m_hWnd); ::ShowWindow(m_hWnd, SW_SHOWNORMAL); ::EnableWindow(hWndParent, FALSE); MSG msg = { 0 }; while( ::IsWindow(m_hWnd) && ::GetMessage(&msg, NULL, 0, 0) ) { if( msg.message == WM_CLOSE && msg.hwnd == m_hWnd ) { nRet = msg.wParam; ::EnableWindow(hWndParent, TRUE); ::SetFocus(hWndParent); } if( !CPaintManagerUI::TranslateMessage(&msg) ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } if( msg.message == WM_QUIT ) break; } ::EnableWindow(hWndParent, TRUE); ::SetFocus(hWndParent); if( msg.message == WM_QUIT ) ::PostQuitMessage(msg.wParam); return nRet;}
可以发现先调用了ShowWindow去显示窗口,然后进入了我们熟悉的GetMessage消息循环。考虑到可能是子窗口关闭WM_CLOSE 所以有些额外处理,CPaintManagerUI这玩意比较庞大,包揽了绘画细节和消息管理, 我们这集还不打算修理它,留到后面解剖。只需要知道TranslateMessage是进行了一个消息预处理过滤的功能。
三、窗口过程
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ CWindowWnd* pThis = NULL; if( uMsg == WM_NCCREATE ) { LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam); pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams); pThis->m_hWnd = hWnd; ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis)); } else { pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); if( uMsg == WM_NCDESTROY && pThis != NULL ) { LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam); ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L); if( pThis->m_bSubclassed ) pThis->Unsubclass(); pThis->m_hWnd = NULL; pThis->OnFinalMessage(hWnd); return lRes; } } if( pThis != NULL ) { return pThis->HandleMessage(uMsg, wParam, lParam); } else { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); }}
窗口过程因为是回调函数,所以声明成static类型,static类型是不能使用非static成员的,那么问题来了,他怎么去获取CWindowWnd对象指针,然后去调用CWindowWnd里面的方法呢。这就是之前CreateWindowEx将this指针传进去的原因了,在WM_NCCREATE时通过将lParam转化成LPCREATESTRUCT,里面的lpCreateParams就是this指针了,然后通过SetWindowLongPtr将this设置为用户数据,再处理其它的WM_消息时通过GetWindowLongPtr获取到this指针,进而可以调用CWindowWnd的方法了(比如这里调用了HandleMessage来处理感兴趣的WM_消息
),这是我们自己封装窗口类难以想到的一点吧(小tips:在声明回调函数时我们一般将最后一个参数设置为用户数据
)。当然有的同学会说使用map容器将HWND与CWindowWnd对应起来,这也是一种方法,但总归没有使用用户数据来的直接简便。
这集就先pass了CWindowWnd类,一步一个脚印我们终将走出DuiLib的沙漠。
- DuiLib教程--从win32窗口开始
- duilib进阶教程 -- 改进窗口拖动 (12)
- duilib进阶教程 改进窗口的拖动
- Ext2.0教程三:Ext2.0从新建窗口开始
- Ext2.0教程三:Ext2.0从新建窗口开始
- DuiLib(1)——简单的win32窗口
- 2013 duilib入门简明教程 -- 结合win32和MFC (16)
- Win32汇编--开始了解窗口程序
- duilib教程
- duilib-封装win32控件
- 从win32控制台创建窗口过程
- NIO教程----从ServerBootstrap开始
- duilib 窗口问题
- Duilib登录窗口
- DUILIB创建菜单窗口
- duilib : 窗口拖动
- duilib : 模态窗口
- DUILIB异形窗口实现
- VC2008编译FFmpeg
- stringstream 函数产生随机数字字符串
- js对话框弹窗
- 正确使用String、StringBuffer、StringBuilder
- 黑马程序员 八、集合框架(2)
- DuiLib教程--从win32窗口开始
- 通过安装memadmin对memcache进行可视化管理
- 多个字符串的输出(指针数组、二级指针)
- 数据类型溢出
- Chrome 控制台console的用法(学了之后对于调试js可是大大有用的哦)
- 人口流向逆转带来什么变化(zz)
- 游戏开发完整学习路线(各个版本都有)
- 黑马程序员 九、IO 操作(1)
- list集合转为json,前台ajax获取时间数据并格式化