所有的控件类都继承于elibstl::CCtrlBase
,这个类包含了一些控件通用属性(文本、字体、边框和图片等),同时还有以下方法
SIZE_T InitBase0(LPVOID pAllData, int cbData, BOOL bInDesignMode, DWORD dwWinFormID, DWORD dwUnitID);
(重载1)
窗口创建前执行的初始化操作,返回基础数据大小
void InitBase0(PCVOID pAllData);
(重载2)
窗口创建后执行的初始化操作
HGLOBAL FlattenInfoBase0(SIZE_T cbExtra = 0u, SIZE_T* pcbBaseData = NULL);
平面化数据,将基础数据保存到HGLOBAL
内存中,cbExtra
指定基础数据之外的额外内存大小,pcbBaseData
接收基类数据大小(因为有文本和图片,所以是变长的)
eStlInline void Redraw();
重画
eStlInline void FrameChanged();
重新核算非客户区
eStlInline HWND GetHWND();
取窗口句柄
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
,供没有标题和图片的控件使用。
这个类引用外部的两个哈希表(设为静态成员),分别是"控件窗口句柄->控件类指针"和"父窗口句柄->父窗口引用计数"。
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
,此类不记录父窗口,也不对父窗口执行子类化。
注意
|
控件类示例如下:
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_CtrlSCInfo
和m_ParentSCInfo
,子类化管理类成员名为m_SM
。
SUBCLASS_MGR_INIT
初始化上述三个静态成员,后两个参数分别为子类化ID。子类化ID应尽量不重复,统一为以日期开头,后加特定序号,如:#define SCID_EDIT 20230501'01u
。
注意 若使用简单子类化管理类
|
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