用Win32++库编写图形界面

最近一直在找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(&gtoken, &ginp, 0);
    CMyApp app;
    return app.Run();
}

吃饱撑着的可以自己去弄个Win32++库编译来玩一下,记得工程开成Unicode。这库只有头文件不用编译生成lib再链接的,这一点来说是很方便。

发表评论