📄 minigui 体系结构之二 多窗口管理和控件及控件类.htm
字号:
<TBODY>
<TR>
<TD><PRE>清单 4 控件的子类化
#define IDC_CTRL1 100
#define IDC_CTRL2 110
#define IDC_CTRL3 120
#define IDC_CTRL4 130
#define MY_ES_DIGIT_ONLY 0x0001
#define MY_ES_ALPHA_ONLY 0x0002
static WNDPROC old_edit_proc;
static int RestrictedEditBox (HWND hwnd, int message, WPARAM wParam, LPARAM lParam)
{
if (message == MSG_CHAR) {
DWORD my_style = GetWindowAdditionalData (hwnd);
/* 确定被屏蔽的按键类型 */
if ((my_style & MY_ES_DIGIT_ONLY) && (wParam < '0' || wParam > '9'))
return 0;
else if (my_style & MY_ES_ALPHA_ONLY)
if (!((wParam >= 'A' && wParam <= 'Z') || (wParam >= 'a' && wParam <= 'z')))
/* 收到被屏蔽的按键消息,直接返回 */
return 0;
}
/* 由老的窗口过程处理其余消息 */
return (*old_edit_proc) (hwnd, message, wParam, lParam);
}
static int ControlTestWinProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case MSG_CREATE:
{
HWND hWnd1, hWnd2, hWnd3;
CreateWindow (CTRL_STATIC, "Digit-only box:", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0,
10, 10, 180, 24, hWnd, 0);
hWnd1 = CreateWindow (CTRL_EDIT, "", WS_CHILD | WS_VISIBLE | WS_BORDER, IDC_CTRL1,
200, 10, 180, 24, hWnd, MY_ES_DIGIT_ONLY);
CreateWindow (CTRL_STATIC, "Alpha-only box:", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0,
10, 40, 180, 24, hWnd, 0);
hWnd2 = CreateWindow (CTRL_EDIT, "", WS_CHILD | WS_BORDER | WS_VISIBLE, IDC_CTRL2,
200, 40, 180, 24, hWnd, MY_ES_ALPHA_ONLY);
CreateWindow (CTRL_STATIC, "Normal edit box:", WS_CHILD | WS_VISIBLE | SS_RIGHT, 0,
10, 70, 180, 24, hWnd, 0);
hWnd3 = CreateWindow (CTRL_EDIT, "", WS_CHILD | WS_BORDER | WS_VISIBLE, IDC_CTRL2,
200, 70, 180, 24, hWnd, MY_ES_ALPHA_ONLY);
CreateWindow ("button", "Close", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, IDC_CTRL4,
100, 100, 60, 24, hWnd, 0);
/* 用自定义的窗口过程替换编辑框的窗口过程,并保存老的窗口过程。*/
old_edit_proc = SetWindowCallbackProc (hWnd1, RestrictedEditBox);
SetWindowCallbackProc (hWnd2, RestrictedEditBox);
break;
}
...
}
return DefaultMainWinProc (hWnd, message, wParam, lParam);
}
</PRE></TD></TR></TBODY></TABLE>
<P>在清单 4 中,程序首先定义了一个窗口处理过程,即 RestrictedEditBox 函数。然后,在利用 CreateWindow
函数建立控件时,将其中两个编辑框的窗口处理过程通过 SetWindowCallbackProc 替换成了自己定义的
RestrictedEditBox 函数,并且将该函数返回的值(即老的控件窗口处理过程地址)保存在了 old_edit_box
变量中。在建立这些编辑框之后,它们的消息将首先由 RestrictedEditBox 函数处理,然后在某些情况下才由老的窗口处理过程处理。</P>
<P>限于篇幅,另外两种控件子类化的方法就不在这里讲述。</P><STRONG>4.2 MiniGUI 中控件类的实现</STRONG>
<P>MiniGUI
函数库实际维护了一个当前所有控件类的数据结构,其中包含了控件类名称以及对应的控件类信息。该数据结构实际是一个哈希表,哈希表的每个入口包含由一个指针,该指针指向所有名程以某个字母开头(不分大小写)的控件类信息链表。控件类信息结构定义如下:</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>#define MAXLEN_CLASSNAME 15
typedef struct _CTRLCLASSINFO
{
char name [MAXLEN_CLASSNAME + 1];
// 控件类名程
/*
* common properties of this class
*/
DWORD dwStyle; // 控件类风格
HCURSOR hCursor; // 控件光标
int iBkColor; // 控件的背景颜色
int (*ControlProc)(HWND, int, WPARAM, LPARAM);
// 控件处理过程
DWORD dwAddData; // 附加数据
int nUseCount; // 使用计数,即系统中属于该控件类的控件个数
struct _CTRLCLASSINFO* next;
// 下一个控件类信息结构
} CTRLCLASSINFO;
typedef CTRLCLASSINFO* PCTRLCLASSINFO;
</PRE></TD></TR></TBODY></TABLE>
<P>在控件类的数据结构中包含了鼠标、光标、控件类的回调函数地址等等信息。在创建属于该控件类的控件时,这些信息会复制到控件数据结构中。这样,新的控件实例就继承了这种控件类的表象和行为。</P>
<P>该哈希表的哈希函数实际非常简单,它的返回值就是控件类名称首字母的英文字母表顺序值:</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>static int HashFunc (char* szClassname)
{
/* 判断首字符是否为字母 */
if (!isalpha (szClassName[0])) return ERR_CTRLCLASS_INVNAME;
/* 讲所有字符转换为大写 */
while (szClassName[i]) {
szClassName[i] = toupper(szClassName[i]);
i++;
if (i > MAXLEN_CLASSNAME)
return ERR_CTRLCLASS_INVLEN;
}
/* 获得哈希值 */
return szClassName[0] - 'A';
}
</PRE></TD></TR></TBODY></TABLE>
<P>控件类的注册和注销函数非常简单,这里不再赘述。</P><STRONG>4.3 MiniGUI 中控件的实现</STRONG>
<P>控件结构相对复杂一些。其中包含了控件在父窗口中的位置信息、控件风格、扩展风格、控件鼠标、图标、控件回调函数地址等等:</P>
<TABLE class=code-sample cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>typedef struct _CONTROL
{
/*
* 这些成员和 MAINWIN 结构一致.
*/
short DataType; // 内部使用的数据类型
short WinType; // 内部使用的窗口类型
int left, top; // 控件在父窗口中的位置
int right, bottom;
int cl, ct; // 控件客户区在父窗口中的位置
int cr, cb;
DWORD dwStyle; // 控件风格
DWORD dwExStyle; // 控件扩展风格
int iBkColor; // 背景颜色
HMENU hMenu; // 菜单句柄
HACCEL hAccel; // 加速键表句柄
HCURSOR hCursor; // 鼠标光标句柄
HICON hIcon; // 图标句柄
HMENU hSysMenu; // 系统菜单句柄
HDC privCDC; // 私有 DC 句柄
INVRGN InvRgn; // 控件的无效区域
PGCRINFO pGCRInfo; // 控件的全局剪切区域
PZORDERNODE pZOrderNode;
// Z 序节点
// 仅对具有 WS_EX_CTRLASMAINWIN 扩展风格的控件有效
PCARETINFO pCaretInfo; // 插入符消息
DWORD dwAddData; // 控件附加数据
DWORD dwAddData2; // 控件附加数据
int (*ControlProc) (HWND, int, WPARAM, LPARAM); // 控件消息处理过程
char* spCaption; // 控件标题
int id; // 控件标识符,整数
SCROLLBARINFO vscroll; // 垂直滚动条信息
SCROLLBARINFO hscroll; // 水平滚动条信息
PMAINWIN pMainWin; // 包含该控件的主窗口
struct _CONTROL* pParent;// 控件的父窗口
/*
* Child windows.
*/
struct _CONTROL* children;
// 控件的第一个子控件
struct _CONTROL* active;
// 当前活动子控件
struct _CONTROL* old_under_pointer;
// 老的鼠标鼠标所在子控件
/*
* 下面这些成员只对控件有效
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -