一起来搭建像Qt一样的win32图形框架

来源:互联网 发布:淘宝充值平台进价表 编辑:程序博客网 时间:2024/04/27 03:08

哎,就文章标题就纠结的很。其实就是觉得Qt的图形框架很爽,但有时候想写一个工具,用Qt带的dll就一堆,有点不爽。又不想用MFC,所以就有了这篇文章。

大家都知道在Qt中的main函数超级简单

int main(){  QApplication app;  MainWindow w;  w.show();  return app.exec();}

大家是不是觉得Qt做得很棒呢!如果我们的win32可以这样,那不是也好爽。下面我们就一步一步来搭建这个框架。

首先用VS建立一个win32工程。为了说明,我调整了代码并删除了一些不必要的代码。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){switch (message){case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,                     _In_opt_ HINSTANCE hPrevInstance,                     _In_ LPTSTR    lpCmdLine,                     _In_ 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 = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT7));wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);wcex.lpszClassName = L"123";wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));RegisterClassEx(&wcex);HWND hWnd = CreateWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);HACCEL hAccelTable = 0;MSGmsg;// 主消息循环: while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return (int) msg.wParam;}

从上面的代码需要关注几个关键点:

1 调用RegisterClassEx注册窗口类。只需要调用一次。

2 调用CreateWindow创建窗口。

3 使用GetMessage进行消息循环。

4 实现窗口的回调函数WndProc。

如果要实现像Qt那样的架构。RegisterClassEx当然要放到Application中,CreateWindow当然要放到MainWindow中,因为可以创建多个窗口,GetMessage也只能放到Application中。准确地说是放到Application的exec中。至于WndProc放到哪,现在先不管。

千言万语,不如代码来得实在。下面我们来时Application类类似Qt的QApplication类。修改后的代码如下:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){switch (message){case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;}class Application{public:Application(){WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = NULL;wcex.hIcon = NULL;wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);wcex.lpszClassName = L"123";wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));RegisterClassEx(&wcex);}int exec(){HACCEL hAccelTable = 0;MSGmsg;while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return (int)msg.wParam;}};int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,                     _In_opt_ HINSTANCE hPrevInstance,                     _In_ LPTSTR    lpCmdLine,                     _In_ int       nCmdShow){Application app;HWND hWnd = CreateWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return app.exec();}

哈哈,是不是发现main函数一下子简单了好多,并且有点像Qt的框架了!别着急,我们继续。

CreateWindow放到一个窗口类中这可以搞定,但如何把回调函数也放到里面呢?怎么办?怎么办?嘿,我想到一个好办法,在Application实现一个static函数作为全局的回调函数,然后CreateWindow时记录所有窗口的句柄,然后Application的Proc不是就可以分发消息到不同的窗口类了嘛!呵呵,说干就干。


#include <cassert>
#include <unordered_map>

class Wnd;

class Application{
public:
 Application(){
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc = Application::proc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = NULL;
  wcex.hIcon = NULL;
  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
  wcex.lpszClassName = L"123";
  wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
  RegisterClassEx(&wcex);
 }

 int exec(){
  HACCEL hAccelTable = 0;
  MSG  msg;
  while (GetMessage(&msg, NULL, 0, 0))
  {
   if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
  }
  return (int)msg.wParam;
 }

 static void registerWnd(Wnd* wnd);
 static void unregisterWnd(Wnd* wnd);
 static Wnd* wnd(HWND hWnd);
 static HRESULT CALLBACK proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
 static std::unordered_map<HWND, Wnd*> s_wnds;
};
std::unordered_map<HWND, Wnd*> Application::s_wnds;

class Wnd{
public:
 Wnd() :_hWnd(NULL){}
 virtual ~Wnd(){
  Application::unregisterWnd(this);
  DestroyWindow(_hWnd);
 }
 void createWindow(LPCWSTR className, LPCWSTR windowName, DWORD style, int x, int y, int w, int h, HWND hParent, HMENU hMenu){
  _hWnd = CreateWindow(className, windowName, style, x, y, w, h, hParent, hMenu,
   GetModuleHandle(NULL), 0);
  Application::registerWnd(this);
 }
 HWND hWnd()const{ return _hWnd; }

 void show(){ ShowWindow(_hWnd, SW_SHOW); }

 virtual HRESULT proc(UINT message, WPARAM wParam, LPARAM lParam){ return 0; }

protected:
 HWND _hWnd;
};

void Application::registerWnd(Wnd* wnd){
 assert(wnd);
 s_wnds[wnd->hWnd()] = wnd;
}
void Application::unregisterWnd(Wnd* wnd){
 auto itr = s_wnds.find(wnd->hWnd());
 if (itr != s_wnds.end()){
  s_wnds.erase(itr);
 }
}
Wnd* Application::wnd(HWND hWnd){
 auto itr = s_wnds.find(hWnd);
 if (itr != s_wnds.end())
  return itr->second;
 return nullptr;
}
HRESULT CALLBACK Application::proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
 auto wnd = Application::wnd(hWnd);
 if (wnd){
  return wnd->proc(message, wParam, lParam);
 }
 return DefWindowProc(hWnd, message, wParam, lParam);
}

class MainWindow :public Wnd{
public:
 MainWindow(){
  createWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL);
 }

 HRESULT proc(UINT message, WPARAM wParam, LPARAM lParam){
  switch (message)
  {
  case WM_DESTROY:
   PostQuitMessage(0);
   break;
  default:
   return DefWindowProc(_hWnd, message, wParam, lParam);
  }
  return 0;
 }
};


int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
 Application app;

 MainWindow w;
 w.show();

 return app.exec();
}

看,现在基本上像Qt了,注意注册类时的回调函数已经变了哈!现在集成MainWindow然后实现proc处理函数就好了,当然还有更好的方法!好了,其实接下来还有好多工作,比如按钮等控件的布局,类似QLayout,按钮等事件如mouseClickEvent等。我要回家了,改天在写吧!




0 0
原创粉丝点击