Step by step——WinX开发入门教程-6

来源:互联网 发布:帝国cms还原数据 编辑:程序博客网 时间:2024/05/16 07:12

自定义窗口类(WNDCLASS)程序

对于对话框模板资源的程序比较简单,因为windows系统帮我们做了很多事情,真的是很多事情,想想我们自己手工用代码动态生成对话框上的一些控件试试看,对话框程序基本工作都交给了windows窗口系统了。我们只需要调用一个函数DialogBox,指定对话框资源ID,窗口过程,OK了。Windows窗口系统自动帮我们从可执行文件的资源节读取数据,帮我们创建窗口上的控件,排列位置。更重要的是,对于模态对话框,我们不需要写消息循环的处理代码。所以前面的例子里,代码都非常简单。对于非模态窗口或者自定义的窗口类窗口,就没这么简单了。

接下来,我们就要完全掌控我们自己的程序,注册我们自己的窗口类窗口。经过前面章节的带领,我们应该比较熟悉winx的脾气了,后面的章节,不需要以灌的方式直接说明怎么做了,大不了,看看winx源代码。不知道谁说的,源代码之下,了无秘密。

回顾

创建一个典型的窗口应用程序,分5步:

a. 写好窗口过程函数

b. 注册窗口类RegisterClass,这个函数用到了WndClass数据结构,该结构主要关注窗口过程

c. 创建窗口CreateWindow,用前面创建的窗口类,加上指定大小位置等信息创建一个窗口。

d. 显示窗口

e. 窗口消息循环

如果不熟悉,请自行学习之。

思考

Winx提供了模态对话框类ModelDialog,提供了非模态对话框类ModelessDialog,那么一定会提供自定义窗口类。反正winx代码也不多,搜索了一番,发现frame.h,从中找到了MainFrame类,应该就是我所需要的。

Winx也一定会秉持能替我们做的就替我们做了的原则,帮我们做大部分事情。窗口过程和对消息的处理,是winx的精华之所在,所以这事不用我们操心,还是遵循消息处理函数命名原则就会被调用起来。

RegisterClass需要用户主动调用,将WNDCLASS的概念暴露,这是winx的设计理念,所以需要我们自己完成。那winx是让我们自己调用::RegisterClass呢还是帮我们调用::RegisterClass呢?我猜测,winx帮我们调用。那窗口类名,窗口style等等数据,如何交给winx处理呢?我猜测,winx向我们要这些数据。看看源代码吧:

class window ...

{

static ATOM winx_call RegisterClass(

IN HINSTANCE hInst = GetThisModule())

{

WNDCLASSEX wcex =

{

sizeof(WNDCLASSEX), // cbSize

WindowClass::GetClassStyle(), // style

WindowClass::WindowProc, // lpfnWndProc

0, // cbClsExtra

0, // cbWndExtra

hInst, // hInstance

NULL, // hIcon

WindowClass::GetDefaultCursor(),// hCursor

WindowClass::GetDefaultBkgnd(), // hbrBackground

NULL, // lpszMenuName

WindowClass::GetWndClassName(), // lpszClassName

NULL // hIconSm

};

return ::RegisterClassEx(&wcex);

}

}

这是window基类的RegisterClass函数。需要我们主动调用的就是这个函数。正如我所猜测的那样,RegisterClass所需要的数据,通过向我们询问来得到。

WindowClass::GetClassStyle(), // style

WindowClass::WindowProc, // lpfnWndProc

WindowClass::GetDefaultCursor(),// hCursor

WindowClass::GetDefaultBkgnd(), // hbrBackground

WindowClass::GetWndClassName(), // lpszClassName

WindowClass,就是我们自己定义的窗口类型了,这是类模板参数。这几个函数,不知道window的继承树里是否都实现了,嗯,这里是考C++语法的时候,overloadoverrideoverwrite这几个东东的差别自行搜索吧。我也不完全明白,大意就是GetClassStyle函数,如果基类也实现了,当然,不是virtual继承类也实现了,则GetClassStyle()调用的是子类的函数,基类的相对于隐藏起来了。如果子类没有实现,就从子类开始,一级一级往上找,找到了就用它。OkRegisterClass了解了。

CreateWindow呢?想必由winx完成了吧。搜索代码,“CreateWindow”,include/winx/win/message.h中,

template 

class WindowMessage // : public MessagePrototype

{

....

// --> CreateWindow

HWND winx_call Create(

HWND hWndParent = NULL, LPCTSTR szCaption = NULL,

int x = CW_USEDEFAULT, int y = CW_USEDEFAULT,

int nWidth = CW_USEDEFAULT, int nHeight = CW_USEDEFAULT,

int dwStyle = -1, int dwExStyle = -1,

MENUorID hMenuOrID = 0, HINSTANCE hInst = GetThisModule())

{

enum { DefaultWindowStyle = WindowClass::DefaultWindowStyle };

enum { DefaultWindowExStyle = WindowClass::DefaultWindowExStyle };

if (dwStyle == -1)

dwStyle = DefaultWindowStyle;

if (dwExStyle == -1)

dwExStyle = DefaultWindowExStyle;

return ::CreateWindowEx(

dwExStyle,

WindowClass::GetWndClassName(),

szCaption,

dwStyle,

x, y, nWidth, nHeight,

hWndParent,

(HMENU)hMenuOrID,

hInst,

_WINX_PWND

);

}

}

嗯,函数Create帮我们完成了。同样需要向我们要几个数据:

WindowClass::DefaultWindowStyle()

WindowClass::DefaultWindowExStyle()

WindowClass::GetWndClassName(),

最后消息循环怎么处理呢?由哪部分完成呢?搜索“TranslateMessage即可。

Include/winx/basic.h  RunMsgLoop函数调用了。如下:

// -------------------------------------------------------------------------

// RunMsgLoop

#define WINX_MSGF_MODELDLG 0

#define WINX_MSGF_BASE 0x8100

#define WINX_MSGF_MAINLOOP (WINX_MSGF_BASE + 1)

inline int winx_call RunMsgLoop(int codeMsgf = WINX_MSGF_MAINLOOP)

{

for(;;)

{

MSG msg;

BOOL bRet = ::GetMessage(&msg, NULL, 0, 0);

if (bRet == -1)

{

WINX_TRACE("GetMessage returned -1 (error)/n");

continue// error, don't process

}

if (!bRet)

{

WINX_TRACE("RunMsgLoop - exiting/n");

return (int)msg.wParam; // WM_QUIT, exit message loop

}

if (!::CallMsgFilter(&msg, codeMsgf))

{

::TranslateMessage(&msg);

::DispatchMessage(&msg);

}

}

}

嗯,看来不用我们自己手工写消息循环了。就用这东东吧。

基本构件都找到了,可以开始我们自己的代码旅程了。

步骤

创建一个空的win32应用程序。

新建一个cpp文件,内容如下:

#include "winx.h"

template <class WindowClass, class HandleClass = winx::DefaultWindowHandle>

class CWYMainFrame : public winx::MainFrame

{

};

class CMyMainWnd : public CWYMainFrame

{

public:

static TCHAR* GetWndClassName(){ return _T("wygui_window");}

};

int APIENTRY _tWinMain(HINSTANCE hInstance,

                     HINSTANCE hPrevInstance,

                     LPTSTR    lpCmdLine,

                     int       nCmdShow)

{

CMyMainWnd wnd;

wnd.RegisterClass();

wnd.Create(NULL,_T("标题") );

wnd.ShowWindow( SW_SHOW );

RunMsgLoop();

return 0;

}

分析

template <class WindowClass, class HandleClass = winx::DefaultWindowHandle>

class CWYMainFrame : public winx::MainFrame

{

};

这是一个空的子类,什么都不做。为什么要这么演示呢?假设我现在想在winx的基础上,开发一套皮肤库来,显然是希望用户继承自我的窗口了。在我的这个窗口类里,我去完成一些事情。

这里需要注意DefaultWindowHandle前面要加上一个winx命名空间名字。第一次写的时候,顺手复制了MainFrame的定义,结果死活编译不过去,提示DefaultWindowHandle找不到,还以为是头文件没有包含进去。

class CMyMainWnd : public CWYMainFrame

{

public:

static TCHAR* GetWndClassName(){ return _T("wygui_window");}

};

这里就实现了一个函数而已。直接返回一个字符串。这是前面分析的结果。注意static关键字。具体原因,以后说到,如果心急想现在知道,自己看看代码就清楚了。

至于其他几个Get函数没有提供,因为winx帮我们做了。

WinMain函数很简单

CMyMainWnd wnd;

wnd.RegisterClass();

wnd.Create(NULL,_T("标题") );

wnd.ShowWindow( SW_SHOW );

RunMsgLoop();

定义一个主窗口,注册,创建,显示,Loop,嗯,基本是SDK的写法,很符合sdk的习惯。但是又很方便,真实爱死winx了。

运行之,结果是一个白板窗口。

Winxhelper 

WINX_CLASS_EX

#define WINX_CLASS_EX(ClassName) /

public: /

static LPCTSTR winx_call GetWndClassName() { /

return ClassName; /

}

该宏往类里插如GetWndClassName,参数为类的字符串。

WINX_CLASS_STYLE

#define WINX_CLASS_STYLE(style) /

public: /

static UINT winx_call GetClassStyle() { return (style); } /

private:

该宏定义wndclass的类型。

窗口类型的宏

#define WINX_DEFAULT_STYLE(style) /

public: /

enum { DefaultWindowStyle = style }

#define WINX_DEFAULT_EXSTYLE(style) /

public: /

enum { DefaultWindowExStyle = style }

一个无标题栏的窗口

我们定义一个无标题栏的窗口玩玩。

class CMyMainWnd : public CWYMainFrame

{

public:

//static TCHAR* GetWndClassName(){ return _T("wygui_window");}

 

WINX_CLASS_EX( _T("wygui_window") );

public:

UINT winx_msg OnNcHitTest(HWND hWnd, winx::CPoint point)

{

return HTCAPTION;

}

};

int APIENTRY _tWinMain(HINSTANCE hInstance,

                     HINSTANCE hPrevInstance,

                     LPTSTR    lpCmdLine,

                     int       nCmdShow)

{

CMyMainWnd wnd;

wnd.RegisterClass();

wnd.Create(NULL,_T("标题") ,300,400,300,300,WS_POPUP | WS_SYSMENU | WS_SIZEBOX);

wnd.ShowWindow( SW_SHOW );

RunMsgLoop();

return 0;

}

需要说明的是,一般winx的宏会在结束的时候加一个private:导致后面跟着的东西变成了私有的。

WS_POPUP | WS_SYSMENU | WS_SIZEBOX这类型可以随意组合。在Create的时候可以带窗口style,也可以不带参数,填-1,则会使用默认的style。我们可以自己定义一个默认的style,方法是用宏。可以试试看。

原创粉丝点击