上一篇文章中,我们已经实现了一个最简单的功能,这一篇文章我们一起来实现控件的添加。以按钮作为例子来实现。
首先我们来实现一个按钮类。
class PushButton :public Wnd{
public:
PushButton(LPCWSTR windowName, int id, Wnd* parent){
createWindow(L"button", windowName, BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE, 0, 0, 0, 0, parent->hWnd(), HMENU(id));
}
};
按钮类PushButton继承自Wnd,windowName表示按钮上显示的文字,id表示按钮的ID,parent表示父窗体。调整我们的main函数如下:
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
Application app;
MainWindow w;
PushButton btn1(L"btn1", 1, &w);
PushButton btn2(L"btn2", 1, &w);
btn1.resize(50, 30);
btn2.resize(50, 30);
btn2.move(60, 0);
w.show();
return app.exec();
}
注意:resize和move函数实现在Wnd中,代码如下:
void resize(int w, int h){ ::SetWindowPos(_hWnd, 0, 0, 0, w, h, SWP_NOZORDER | SWP_NOMOVE); }
void move(int x, int y){ ::SetWindowPos(_hWnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); }
现状运行程序,可以看到效果如下:
可以看到,按钮已经成功添加到窗口了,那么接下来需要做的事情就是考虑如何响应按钮事件了。个人比较偏爱Qt,因此想这样实现:点击按钮会响应MainWindow的buttonClickEvent函数,然后在函数参数可以指示按钮的id。下面我们实现MainWindow的buttonClickEvent函数:
void buttonClickEvent(int id){
if (id == 1)
MessageBox(_hWnd, L"1", L"", MB_OK);
if (id==2)
MessageBox(_hWnd, L"2", L"", MB_OK);
}
然后调整MainWindow的proc函数:
HRESULT proc(UINT message, WPARAM wParam, LPARAM lParam){
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
buttonClickEvent(LOWORD(wParam));
break;
default:
return DefWindowProc(_hWnd, message, wParam, lParam);
}
return 0;
}
亲们请注意:这里我指示为了演示,WM_COMMAND下并不一定是按钮事件哟!
现在点击按钮,是不是有反应了呢!!!其实这里buttonClickEvent函数的参数最好封装成一个Event类,像Qt那样,这里只是演示,也足够了。到目前为止,框架基本完成了,相信亲们可以举一反三,实现其他控件如Label等的创建和其他事件的响应。
其实现在的代码很乱。尤其是main函数,有违我们的初衷。让我们来整理一下:将Application类和Wnd类单独放到一个文件里,PushButton类和MainWindow也各自单独放到一个文件里,将MainWindow的buttonClickEvent函数定义为虚函数并删除实现代码。然后我们在main.cpp中重新定义一个类MyMainWindow并继承自MainWindow:
class MyMainWindow :public MainWindow{
public:
MyMainWindow() :MainWindow()
, btn1(L"btn1",1,this)
, btn2(L"btn2", 2, this)
{
btn1.resize(50, 30);
btn2.resize(50, 30);
btn2.move(60, 0);
}
void buttonClickEvent(int id){
if (id == 1)
MessageBox(_hWnd, L"1", L"", MB_OK);
if (id == 2)
MessageBox(_hWnd, L"2", L"", MB_OK);
}
private:
PushButton btn1;
PushButton btn2;
};
现在main函数如下:
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
Application app;
MyMainWindow w;
w.show();
return app.exec();
}
功能一样,但main函数又清静了,并且现在整个程序的架构和Qt非常相似!在整个框架中,Application,Wnd,MainWIndow属于基本的模块,功能大家可以逐步完善,例如像MainWindow的事件,目前只有WM_COMMAND的按钮事件。PushButton属于控件,也需要不断扩展。以后的程序可以直接都继承MainWindow,然后响应响应的功能即可,比如有按钮,重新实现buttonClickEvent函数即可。main函数都一样。不需要额外处理了。整个程序的处理全部都集中在了继承自MainWindow那个类。不知道亲们有没有收获呢?
结语:其实在真正的实际运用中,考虑的情况更多,之前这两篇文章主要是一起探讨学习,只关注关键点,没有进行异常处理,也没有考虑代码的通用性。小弟比较喜欢折腾,喜欢重用自己的代码,基本上有一套自己的代码库,上面的win32图像框架也在其中,并且更加完善且会不断完善,支持layout等布局,有兴趣的同学欢迎下载,Qt的库用Q打头,小弟就用X打头好了!
代码库说明:
src目录下为只依赖C++库的C++代码,win32下就是这两篇文章的图形框架。所有代码主要以hpp形式组织。
win32下图形库代码示例:
#include "win32/XApplication.hpp"
#include "win32/XMainWindow.hpp"
#include "win32/XHBoxLayout.hpp"
#include "win32/XPushButton.hpp"
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
XApplication app;
XMainWindow w("test");
XHBoxLayout* layout = new XHBoxLayout(&w);
layout->addWidget(new XPushButton("123", &w));
layout->addWidget(new XPushButton("456", &w));
w.resize(300, 200);
w.show();
return app.exec();
}
以上代码效果如下:
调整窗体大小是按钮大小也会自动调整,类似Qt,具体用法也和Qt类似。这里没有继承XMainWindow只是为了演示,如果是真正需要实现实际功能的程序,最好还是继承XMainWindow,然后在这里类里面完成所有的逻辑。