最近一直在找C++编写图形界面的库。用C#确实方便,拖拖控件就出来了。但是对.net Framework运行库的依赖有的时候是略烦人的。于是想掌握不需要这个就能写图形界面的方法。用Windows API编写图形界面不是说不会,就是麻烦,忒麻烦(擦汗
关于现成的各种库,用过Ultimate++、FLTK等。Ultimate++在MinGW下表现不错,看起来也很native虽然是自己画的。但是换VC就半死不活了:英文下没问题,中文的话,哼哼……它源代码是保存成UTF8格式的,里面字符串全都是raw UTF8,就等着VC报错吧 -_,- FLTK的话,win95你一脸。为了静态链接,还用了好多手段好不容易调成静态链接。看着那个界面,最终还是放弃了……而且它的fluid界面设计器生成的代码,里面的字符串遇到中文的全部给你艹成斜杠加八进制数,略烦。VCF没有接触更多,没找到它的图形界面设计器,暂时还是算了……
WTL我也尝试过,用起来怎么说呢……反正也就那德行-_,- 关键是文档,WTL的文档很烂,很多都找不来,得去看代码。对于我等没文档写不了代码的人来说……Oh my god。WxWidgets的设计哲学跟我各种不符,在Code::Block里面试了一下,虽然是有设计器,但是那个设计器不知道为什么,说不出的别扭,拖了两下,运行程序,控件乱成屎了(满脸血)。
其实为什么尝试的里面没看到很著名的那些库呢?其实相关的考虑,一个是LGPL协议:QT库遵循LGPL协议,不买不给你静态链接;GTK就干脆连买的机会也木有的样子?MFC主要还是收费吧,不像是我等现在可以买得起来自己玩的,虽然给公司做的话是完全没问题……Borland那个C++ Builder的库同理。
死找了这么久,今天下午终于决定静下心来试一下Win32++。这东西据说是挺像MFC的(虽然其实MFC我一点都不熟,接近是不会的程度)。跟着教学走下来,到第二个还是第三个章节创建出空窗口的程度以后,好了不耐烦了,开始自己玩了(殴
于是花了一整个下午,没错……用C#几分钟就出来的东西,用了一整个下午,非常整的那种,不知道是不是中午吃饭前的一小段时间也要赔进去的一整个下午,出来了这么一个无法形容的蹩脚物
这就是“哇!计算器(暂定)”。
怎么样,屌爆(dànténg)极了是不是!没错你没有看错,它就只有这么一点功能,给出两个数字,算加法,你乱输入它甚至连个错误信息都不会给出的哇!计算器(暂定),一整个下午(扶额
主要卡我的地方,一个是上面那些控件,最早搞出来的文本框全部都有一个问题,就是不像正常控件那样有个边框。解决的办法就是设置Windows Style Extra那个,用clientedge啥的就能出边框了。另一个就是又忘了默认windows style没有visible,所以自定义style以后控件消失。还有就是文本框的文字数限制……这个是搞了我最久的,因为它根据文本框的长短,输入的文字到了文本框最右以后就不让你输入了,设置limit也没用。最后在网上找了好久,才知道要用ES_AUTOHSCROLL。还有关于用tab键在控件之间跳转,光是设置一个WS_TABSTOP还是不够的,还得在消息处理上面下功夫,IsDialogMessage这个函数来搞一搞才出结果。
主要还是想知道一下不用对话框资源的话搞对话框要怎么搞。消息处理那边,各个控件因为都没有id,这点很烦躁。没办法,windows api里面,用CreateWindow的时候那个id是塞在HMENU参数里的,而在win32++里面,用一个CMenu封装了,我尝试强制弄一个HMENU进到CMenu里面去,最终还是失败了:这玩意儿会验证句柄有效性。看来想要利用控件ID来识别的话,不在库里面做一点小小的Hack是不行的。
代码长这样:
#include "wincore.h" #include "stdcontrols.h" #pragma comment(linker,""/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"") class MyEdit : public CEdit { public: void PreCreate(CREATESTRUCT& cs) { cs.dwExStyle |= WS_EX_CLIENTEDGE; cs.style |= ES_AUTOHSCROLL; CEdit::PreCreate(cs); } }; template<class T> class MyCtrl : public T { public: void PreCreate(CREATESTRUCT& cs) { cs.style |= WS_CHILD | WS_VISIBLE | WS_TABSTOP; T::PreCreate(cs); } }; #include <GdiPlus.h> #pragma comment(lib, "gdiplus.lib") class CView : public CWnd { MyCtrl<MyEdit> m_editA, m_editB; MyCtrl<CButton> m_btn; CFont m_cf; public: CView() { } ~CView() { } void PreCreate(CREATESTRUCT& cs) { CWnd::PreCreate(cs); cs.lpszClass = _T("MyApp"); cs.lpszName = _T("WTF"); cs.cx = 280; cs.cy = 160; cs.y = cs.x = CW_USEDEFAULT; cs.style = (WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_VISIBLE) & ~WS_MAXIMIZEBOX; } BOOL PreTranslateMessage(MSG* pMsg) { return IsDialogMessage(pMsg); } LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY: PostQuitMessage(0); break; } return WndProcDefault(uMsg, wParam, lParam); } BOOL OnEraseBkgnd(CDC* dc) { CBrush brush; brush.Attach(GetSysColorBrush(COLOR_3DFACE)); dc->FillRect(GetClientRect(), &brush); return TRUE; } void OnDraw(CDC* dc) { Gdiplus::PointF pos(50, 40); Gdiplus::SolidBrush brush(Gdiplus::Color::Black); Gdiplus::Font f(dc->GetHDC(), (HFONT)m_cf.GetHandle()); Gdiplus::Graphics g(dc->GetHDC()); g.DrawString(_T("这是一个计算器"), -1, &f, pos, &brush); g.Flush(); } BOOL OnCommand(WPARAM wParam, LPARAM lParam) { HWND hwnd = (HWND) lParam; if (hwnd == m_btn.GetHwnd()) { CString csA = m_editA.GetWindowText(); CString csB = m_editB.GetWindowText(); double a, b, c; sscanf(T2A(csA), "%lf", &a); sscanf(T2A(csB), "%lf", &b); c = a + b; CString csC; csC.Format(_T("%lg"), c); MessageBox(csC, _T("结果是"), MB_ICONINFORMATION); } else return FALSE; return TRUE; } void OnCreate() { HFONT font = (HFONT) GetStockObject(DEFAULT_GUI_FONT); m_cf = CFont(font); m_btn.Create(this); m_btn.SetWindowText(_T("哇!")); m_btn.MoveWindow(10, 10, 50, 30); m_btn.SetFont(&m_cf); m_editA.Create(this); m_editA.SetFont(&m_cf); m_editA.MoveWindow(70, 10, 60, 30); m_editB.Create(this); m_editB.MoveWindow(140, 10, 60, 30); m_editB.SetFont(&m_cf); } }; class CMyApp : public CWinApp { public: CMyApp() { } ~CMyApp() { } BOOL InitInstance() { m_view.Create(); return TRUE; } private: CView m_view; }; int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int nShow) { Gdiplus::GdiplusStartupInput ginp; ULONG_PTR gtoken; GdiplusStartup(>oken, &ginp, 0); CMyApp app; return app.Run(); }
吃饱撑着的可以自己去弄个Win32++库编译来玩一下,记得工程开成Unicode。这库只有头文件不用编译生成lib再链接的,这一点来说是很方便。