Skip to content

Latest commit

 

History

History
244 lines (175 loc) · 6.5 KB

控件代码结构说明.md

File metadata and controls

244 lines (175 loc) · 6.5 KB

elibstl————控件代码结构(2023.5.16)

注:如果你觉得现在的结构有任何不合理的地方直接改就可以,改动之后同步更新这个文档。



一、基类

所有的控件类都继承于elibstl::CCtrlBase,这个类包含了一些控件通用属性(文本、字体、边框和图片等),同时还有以下方法

1.

SIZE_T InitBase0(LPVOID pAllData, int cbData, BOOL bInDesignMode, DWORD dwWinFormID, DWORD dwUnitID);

(重载1)
窗口创建前执行的初始化操作,返回基础数据大小

2.

void InitBase0(PCVOID pAllData);

(重载2)
窗口创建后执行的初始化操作

3.

HGLOBAL FlattenInfoBase0(SIZE_T cbExtra = 0u, SIZE_T* pcbBaseData = NULL);

平面化数据,将基础数据保存到HGLOBAL内存中,cbExtra指定基础数据之外的额外内存大小,pcbBaseData接收基类数据大小(因为有文本和图片,所以是变长的)

4.

eStlInline void Redraw();

重画

5.

eStlInline void FrameChanged();

重新核算非客户区

6.

eStlInline HWND GetHWND();

取窗口句柄

7.

eStlInline HWND GetHParent();

取首次创建父窗口

继承之后类的初始化操作一般如下(伪代码):

void InitBase()
{
	auto cbBase = InitBase0();
	
	m_hWnd = CreateWindowExW();
	m_SM.OnCtrlCreate(this);
	m_hParent = hParent;

	InitBase0();
}

继承之后类的平面化数据操作一般如下(伪代码):

HGLOBAL FlattenInfo()
{
	SIZE_T cbBase;
	auto hGlobal = FlattenInfoBase0(额外数据大小, &cbBase);
	p = GlobalLock(hGlobal);
	
	memcpy(p, 数据, 数据大小)
	GlobalUnlock(hGlobal);
	
	return hGlobal;
}

除上述基类外还有一简单基类elibstl::CCtrlBaseSimple,供没有标题和图片的控件使用。

二、定义控件类

1.子类化管理————elibstl::CSubclassMgr

这个类引用外部的两个哈希表(设为静态成员),分别是"控件窗口句柄->控件类指针"和"父窗口句柄->父窗口引用计数"。

a)构造时需要提供哈希表引用、子类化过程和子类化ID,后两个供子类化帮助系列API使用。

b)控件创建后调用OnCtrlCreate,该函数自动将控件窗口和他的父窗口加入哈希表。

c)控件销毁后调用OnCtrlDestroy,该函数将控件从哈希表中删除,并递减父窗口引用计数,若减为0则删除父窗口子类化。

d)父窗口销毁时调用OnParentDestroy,该函数从哈希表中删除父窗口。

e)易语言创建控件时的hParent参数总为顶级窗口句柄,在子类化过程接收到WM_SHOWWINDOW之前父窗口被替换为容器窗口(如果控件是在容器上的话),因为WM_NOTIFY总是发给创建时的父窗口,WM_COMMAND总是发给直接父窗口,所以为了保证两种通知都能接收到,处理WM_SHOWWINDOW需要多加一步判断,具体为:

case WM_SHOWWINDOW:
	CHECK_PARENT_CHANGE;
	break;

其中CHECK_PARENT_CHANGE的定义:

#define CHECK_PARENT_CHANGE \
	if (!p->m_bParentChanged) \
		if (GetParent(hWnd) != p->m_hParent) \
		{ \
			m_SM.OnParentChanged(p); \
			p->m_bParentChanged = TRUE; \
		}

OnParentChanged方法添加新的父窗口到哈希表,同时把它子类化。

除上述类之外还有一简单类CSubclassMgrSimple,此类不记录父窗口,也不对父窗口执行子类化。

注意

CSubclassMgrCSubclassMgrSimple都有OnCtrlCreate2方法,与OnCtrlCreate的区别是该方法不对子窗口进行子类化,但类中仍要定义CtrlSubclassProc以通过语法检查。

2.控件类

控件类示例如下:

class CEdit
{
	SUBCLASS_MGR_DECL(CEdit)
private:
	// 父窗口子类化过程
	static LRESULT CALLBACK ParentSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
	{
		case WM_DESTROY:// 父窗口销毁
			m_SM.OnParentDestroy(hWnd);
			break;

		return DefSubclassProc(hWnd, uMsg, wParam, lParam);
	}
	// 子窗口子类化过程
	static LRESULT CALLBACK CtrlSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
	{
		case WM_DESTROY:// 子窗口销毁
			m_SM.OnCtrlDestroy(p);
			delete p;
			return DefSubclassProc(hWnd, uMsg, wParam, lParam);

		case WM_SHOWWINDOW:// 修正父窗口
			CHECK_PARENT_CHANGE;
			break;

		return DefSubclassProc(hWnd, uMsg, wParam, lParam);
	}
public:
	CEdit() = delete;
	CEdit(STD_ECTRL_CREATE_ARGS)
	{
	}
}
SUBCLASS_MGR_INIT(CEdit, SCID_EDITPARENT, SCID_EDIT)

SUBCLASS_MGR_DECL声明了3个静态成员:两个哈希表和一个 CSubclassMgr,用于管理子类化,哈希表成员名为m_CtrlSCInfom_ParentSCInfo,子类化管理类成员名为m_SM

SUBCLASS_MGR_INIT初始化上述三个静态成员,后两个参数分别为子类化ID。子类化ID应尽量不重复,统一为以日期开头,后加特定序号,如:#define SCID_EDIT 20230501'01u

注意

若使用简单子类化管理类CSubclassMgrSimple,则上述两宏应改为SUBCLASS_SMP_MGR_DECLSUBCLASS_SMP_MGR_INIT

ParentSubclassProcCtrlSubclassProc名称不能改变,因为子类化管理类依赖这个名称。

STD_EINTF_CREATE_ARGS,易语言创建控件接口函数的参数定义。

#define STD_EINTF_CREATE_ARGS \
	LPBYTE pAllData, INT cbData, DWORD dwStyle, HWND hParent, UINT nID, \
	HMENU hMenu, INT x, INT y, INT cx, INT cy, DWORD dwWinFormID, DWORD dwUnitID, HWND hDesignWnd, BOOL bInDesignMode

STD_ECTRL_CREATE_ARGS,控件类构造函数参数定义。 STD_ECTRL_CREATE_REAL_ARGS,控件类构造函数参数(没有类型,就是参数和逗号排排坐)。

#define STD_ECTRL_CREATE_ARGS \
	LPVOID pAllData, int cbData, DWORD dwStyle, int x, int y, int cx, int cy, \
	HWND hParent, UINT nID, BOOL bInDesignMode, DWORD dwWinFormID, DWORD dwUnitID

#define STD_ECTRL_CREATE_REAL_ARGS \
	pAllData, cbData, dwStyle, x, y, cx, cy, \
	hParent, nID, bInDesignMode, dwWinFormID, dwUnitID