编辑框在DELPHI里类名为"TEDIT",在StdCtrls里进行了子类化:CreateSubClass(Params, 'EDIT'),请参数StdCtrls单元里的TCustomEdit.CreateParams方法.
Delphi子类化过程:
procedure TWinControl.CreateSubClass(var Params: TCreateParams;
ControlClassName: PChar);
const
CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS;
CS_ON = CS_VREDRAW or CS_HREDRAW;
var
SaveInstance: THandle;
begin
if ControlClassName <> nil then
with Params do
begin
SaveInstance := WindowClass.hInstance;
if not GetClassInfo(HInstance, ControlClassName, WindowClass) and //当前模块句柄,可能是dll或pkg
not GetClassInfo(0, ControlClassName, WindowClass) and //系统保留类(by the system )
not GetClassInfo(MainInstance, ControlClassName, WindowClass) then //应用程序模块句柄
GetClassInfo(WindowClass.hInstance, ControlClassName, WindowClass);
WindowClass.hInstance := SaveInstance;
WindowClass.style := WindowClass.style and not CS_OFF or CS_ON;
end;
end;
子类化函数,主要是要理解:
BOOL GetClassInfo(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpClassName, // class name
LPWNDCLASS lpWndClass // class data
);
它是将应用程序内部的类的信息提取到lpWndClass中.在CreateSubClass里有用三个句柄取得类信息,0表示系统里.三个判断条件无非就是要取得WindowClass.hInstance,而后再取得真正的类信息,可见delphi真是用心良苦啊.另外他只是将类信息保存到CreateParams里去.
这里特别要注意,delphi所有的winControl类的都含有:
CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS;
CS_ON = CS_VREDRAW or CS_HREDRAW; resize时候进行重画.
再看下windows内部的edit的WndClass定义:
/*********************************************************************
* edit class descriptor
*/
static const WCHAR editW[] = {'E','d','i','t',0};
const struct builtin_class_descr EDIT_builtin_class =
{
editW, /* name */ //还记得昨天菜单窗口的类名32678吗?
CS_DBLCLKS | CS_PARENTDC, /* style */
EditWndProcA, /* procA */
EditWndProcW, /* procW */
sizeof(EDITSTATE *), /* extra */ //注意:窗口扩展数据,不是类扩展数据.那么类扩展数据在那儿定义呢?可以看到Edit扩展结构是EDITSTATE *
IDC_IBEAM, /* cursor */
0 /* brush */
};
这里主要是看 EditWndProcA, EditWndProcW两过程,一个是ASCII的,一个是UNICODE的,他俩都是简单调用:
static LRESULT EditWndProc_common( HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam, BOOL unicode )
{
EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW( hwnd, 0 );//GetWindowLong64位扩展版
LRESULT result = 0;
if (!es && msg != WM_NCCREATE)
return DefWindowProcT(hwnd, msg, wParam, lParam, unicode);
if (es && (msg != WM_DESTROY)) EDIT_LockBuffer(es);
switch (msg) {
case EM_GETSEL: //选中位置
result = EDIT_EM_GetSel(es, (PUINT)wParam, (PUINT)lParam);
break;
case EM_SETSEL: //设置插入符位置
EDIT_EM_SetSel(es, wParam, lParam, FALSE);
EDIT_EM_ScrollCaret(es);
result = 1;
break;
case EM_GETRECT: //取得可画RECT
if (lParam)
CopyRect((LPRECT)lParam, &es->format_rect);
break;
case EM_SETRECT: //设置可画RECT,只对ES_MULTILINE有效
if ((es->style & ES_MULTILINE) && lParam) {
EDIT_SetRectNP(es, (LPRECT)lParam);
EDIT_UpdateText(es, NULL, TRUE);
}
break;
case EM_SETRECTNP: //同上,只是不刷新TEXT
if ((es->style & ES_MULTILINE) && lParam)
EDIT_SetRectNP(es, (LPRECT)lParam);
break;
case EM_SCROLL: //滚动
result = EDIT_EM_Scroll(es, (INT)wParam);
break;
case EM_LINESCROLL: //行滚动
result = (LRESULT)EDIT_EM_LineScroll(es, (INT)wParam, (INT)lParam);
break;
case EM_SCROLLCARET: //滚动插入符
EDIT_EM_ScrollCaret(es);
result = 1;
break;
case EM_GETMODIFY: //是否已经修改过
result = ((es->flags & EF_MODIFIED) != 0);
break;
case EM_SETMODIFY: //设置修改过
if (wParam)
es->flags |= EF_MODIFIED;
else
es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
break;
case EM_GETLINECOUNT: //行数
result = (es->style & ES_MULTILINE) ? es->line_count : 1; //直接返回es->line_count,多行时候,其实WM_PAINT还有考虑滚动
break;
case EM_LINEINDEX: //当前行的偏移
result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
break;
case EM_SETHANDLE: // 重新分配本地堆数据
EDIT_EM_SetHandle(es, (HLOCAL)wParam);
break;
case EM_GETHANDLE: //取得本地堆缓存指针
result = (LRESULT)EDIT_EM_GetHandle(es);
break;
case EM_GETTHUMB: //滚动
result = EDIT_EM_GetThumb(es);
break;
/* these messages missing from specs */
case WM_USER+15:
case 0x00bf:
case WM_USER+16:
case 0x00c0:
case WM_USER+19:
case 0x00c3:
case WM_USER+26:
case 0x00ca:
FIXME("undocumented message 0x%x, please report\n", msg);
result = DefWindowProcW(hwnd, msg, wParam, lParam);
break;
case EM_LINELENGTH: //一行的长度
result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
break;
case EM_REPLACESEL: //替换选中内容
{
LPWSTR textW;
if(unicode)
textW = (LPWSTR)lParam;
else
{
LPSTR textA = (LPSTR)lParam;
INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
}
EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, TRUE, TRUE);
result = 1;
if(!unicode)
HeapFree(GetProcessHeap(), 0, textW);
break;
}
case EM_GETLINE: //当前行
result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPWSTR)lParam, unicode);
break;
case EM_SETLIMITTEXT: //限制字符个数
EDIT_EM_SetLimitText(es, wParam);
break;
case EM_CANUNDO: //恢复取消
result = (LRESULT)EDIT_EM_CanUndo(es);
break;
case EM_UNDO:
case WM_UNDO: //取消
result = (LRESULT)EDIT_EM_Undo(es);
break;
case EM_FMTLINES: //可视区域行数
result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
break;
case EM_LINEFROMCHAR: //可视区域字符数
result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
break;
case EM_SETTABSTOPS: //可停留
result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
break;
case EM_SETPASSWORDCHAR: //设置格式化字符
{
WCHAR charW = 0;
if(unicode)
charW = (WCHAR)wParam;
else
{
CHAR charA = wParam;
MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
}
EDIT_EM_SetPasswordChar(es, charW);
break;
}
case EM_EMPTYUNDOBUFFER: //清空取消缓冲
EDIT_EM_EmptyUndoBuffer(es);
break;
case EM_GETFIRSTVISIBLELINE: //TOPROW
result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
break;
case EM_SETREADONLY: //只读
if (wParam) {
SetWindowLongPtrW( hwnd, GWL_STYLE,
GetWindowLongPtrW( hwnd, GWL_STYLE ) | ES_READONLY );
es->style |= ES_READONLY;
} else {
SetWindowLongPtrW( hwnd, GWL_STYLE,
GetWindowLongPtrW( hwnd, GWL_STYLE ) & ~ES_READONLY );
es->style &= ~ES_READONLY;
}
result = 1;
break;
case EM_SETWORDBREAKPROC: //折行
EDIT_EM_SetWordBreakProc(es, (void *)lParam);
break;
case EM_GETWORDBREAKPROC: //取得是否折行
result = (LRESULT)es->word_break_proc;
break;
case EM_GETPASSWORDCHAR: //格式化字符
{
if(unicode)
result = es->password_char;
else
{
WCHAR charW = es->password_char;
CHAR charA = 0;
WideCharToMultiByte(CP_ACP, 0, &charW, 1, &charA, 1, NULL, NULL);
result = charA;
}
break;
}
/* The following EM_xxx are new to win95 and don't exist for 16 bit */
case EM_SETMARGINS: //只能设置左右的偏移,其实是设置format_rect
EDIT_EM_SetMargins(es, (INT)wParam, LOWORD(lParam), HIWORD(lParam), TRUE);
break;
case EM_GETMARGINS:
result = MAKELONG(es->left_margin, es->right_margin);
break;
case EM_GETLIMITTEXT: //取得限制字符长度
result = es->buffer_limit;
break;
case EM_POSFROMCHAR: //取得某一位置的字符内容
if ((INT)wParam >= get_text_length(es)) result = -1;
else result = EDIT_EM_PosFromChar(es, (INT)wParam, FALSE);
break;
case EM_CHARFROMPOS: //某一行字符的第一个位置
result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
break;
/* End of the EM_ messages which were in numerical order; what order
* are these in? vaguely alphabetical?
*/
case WM_NCCREATE: //非客户区初始
result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTW)lParam, unicode);
break;
case WM_DESTROY: //释放
result = EDIT_WM_Destroy(es);
es = NULL;
break;
case WM_GETDLGCODE: //设置键盘接受字符
result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
if (es->style & ES_MULTILINE)
result |= DLGC_WANTALLKEYS;
if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
{
int vk = (int)((LPMSG)lParam)->wParam;
if (es->hwndListBox)
{
if (vk == VK_RETURN || vk == VK_ESCAPE)
if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
result |= DLGC_WANTMESSAGE;
}
else
{
switch (vk)
{
case VK_ESCAPE:
SendMessageW(GetParent(hwnd), WM_CLOSE, 0, 0);
break;
default:
break;
}
}
}
break;
case WM_IME_CHAR: //输入法字符
if (!unicode)
{
WCHAR charW;
CHAR strng[2];
strng[0] = wParam >> 8;
strng[1] = wParam & 0xff;
if (strng[0]) MultiByteToWideChar(CP_ACP, 0, strng, 2, &charW, 1);
else MultiByteToWideChar(CP_ACP, 0, &strng[1], 1, &charW, 1);
result = EDIT_WM_Char(es, charW);
break;
}
/* fall through */
case WM_CHAR: //处理字符消息
{
WCHAR charW;
if(unicode)
charW = wParam;
else
{
CHAR charA = wParam;
MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
}
if (es->hwndListBox)
{
if (charW == VK_RETURN || charW == VK_ESCAPE)
{
if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0);
break;
}
}
result = EDIT_WM_Char(es, charW);
break;
}
case WM_UNICHAR: //中文字符消息
if (unicode)
{
if (wParam == UNICODE_NOCHAR) return TRUE;
if (wParam <= 0x000fffff)
{
if(wParam > 0xffff) /* convert to surrogates */
{
wParam -= 0x10000;
EDIT_WM_Char(es, (wParam >> 10) + 0xd800);
EDIT_WM_Char(es, (wParam & 0x03ff) + 0xdc00);
}
else EDIT_WM_Char(es, wParam);
}
return 0;
}
break;
case WM_CLEAR: //清楚TEXT
EDIT_WM_Clear(es);
break;
case WM_COMMAND: //通知命令消息
EDIT_WM_Command(es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
break;
case WM_CONTEXTMENU: //右键菜单消息
EDIT_WM_ContextMenu(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
break;
case WM_COPY: //拷贝
EDIT_WM_Copy(es);
break;
case WM_CREATE: //初始化
if(unicode)
result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName);
else
{
LPCSTR nameA = ((LPCREATESTRUCTA)lParam)->lpszName;
LPWSTR nameW = NULL;
if(nameA)
{
INT countW = MultiByteToWideChar(CP_ACP, 0, nameA, -1, NULL, 0);
if((nameW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW);
}
result = EDIT_WM_Create(es, nameW);
HeapFree(GetProcessHeap(), 0, nameW);
}
break;
case WM_CUT: //剪贴
EDIT_WM_Cut(es);
break;
case WM_ENABLE: //设置ENABLED
es->bEnableState = (BOOL) wParam;
EDIT_UpdateText(es, NULL, TRUE);
break;
case WM_ERASEBKGND: //擦除背景
/* we do the proper erase in EDIT_WM_Paint */
result = 1;
break;
case WM_GETFONT: //字体,默认为系统字体
result = (LRESULT)es->font;
break;
case WM_GETTEXT: //这个就不说了
result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPWSTR)lParam, unicode);
break;
case WM_GETTEXTLENGTH: //这个就不说了
if (unicode) result = get_text_length(es);
else result = WideCharToMultiByte( CP_ACP, 0, es->text, get_text_length(es),
NULL, 0, NULL, NULL );
break;
case WM_HSCROLL: //这个就不说了
result = EDIT_WM_HScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
break;
case WM_KEYDOWN: //这个就不说了
result = EDIT_WM_KeyDown(es, (INT)wParam);
break;
case WM_KILLFOCUS: //这个就不说了
result = EDIT_WM_KillFocus(es);
break;
case WM_LBUTTONDBLCLK: //这个就不说了
result = EDIT_WM_LButtonDblClk(es);
break;
case WM_LBUTTONDOWN: //以下都就不说了
result = EDIT_WM_LButtonDown(es, wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
break;
case WM_LBUTTONUP:
result = EDIT_WM_LButtonUp(es);
break;
case WM_MBUTTONDOWN:
result = EDIT_WM_MButtonDown(es);
break;
case WM_MOUSEMOVE:
result = EDIT_WM_MouseMove(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
break;
case WM_PRINTCLIENT:
case WM_PAINT:
EDIT_WM_Paint(es, (HDC)wParam);
break;
case WM_PASTE:
EDIT_WM_Paste(es);
break;
case WM_SETFOCUS:
EDIT_WM_SetFocus(es);
break;
case WM_SETFONT:
EDIT_WM_SetFont(es, (HFONT)wParam, LOWORD(lParam) != 0);
break;
case WM_SETREDRAW:
/* FIXME: actually set an internal flag and behave accordingly */
break;
case WM_SETTEXT:
EDIT_WM_SetText(es, (LPCWSTR)lParam, unicode);
result = TRUE;
break;
case WM_SIZE:
EDIT_WM_Size(es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case WM_STYLECHANGED: //Style切换
result = EDIT_WM_StyleChanged(es, wParam, (const STYLESTRUCT *)lParam);
break;
case WM_STYLECHANGING:
result = 0; /* See EDIT_WM_StyleChanged */
break;
case WM_SYSKEYDOWN:
result = EDIT_WM_SysKeyDown(es, (INT)wParam, (DWORD)lParam);
break;
case WM_TIMER:
EDIT_WM_Timer(es);
break;
case WM_VSCROLL:
result = EDIT_WM_VScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
break;
case WM_MOUSEWHEEL:
{
int gcWheelDelta = 0;
UINT pulScrollLines = 3;
SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
if (wParam & (MK_SHIFT | MK_CONTROL)) {
result = DefWindowProcW(hwnd, msg, wParam, lParam);
break;
}
gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
{
int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
result = EDIT_EM_LineScroll(es, 0, cLineScroll);
}
}
break;
/* IME messages to make the edit control IME aware */
case WM_IME_SETCONTEXT:
break;
case WM_IME_STARTCOMPOSITION:
es->composition_start = es->selection_end;
es->composition_len = 0;
break;
case WM_IME_COMPOSITION:
EDIT_ImeComposition(hwnd, lParam, es);
break;
case WM_IME_ENDCOMPOSITION:
if (es->composition_len > 0)
{
static const WCHAR empty_stringW[] = {0};
EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
es->selection_end = es->selection_start;
es->composition_len= 0;
}
break;
case WM_IME_COMPOSITIONFULL:
break;
case WM_IME_SELECT:
break;
case WM_IME_CONTROL:
break;
default:
result = DefWindowProcT(hwnd, msg, wParam, lParam, unicode);
break;
}
if (es) EDIT_UnlockBuffer(es, FALSE);
TRACE("hwnd=%p msg=%x (%s) -- 0x%08lx\n", hwnd, msg, SPY_GetMsgName(msg, hwnd), result);
return result;
}
以下一一解析:
LONG
WINAPI
GetWindowLongW(HWND hWnd, int nIndex) //这是一个可以改变窗体属性的函数,非常常用
{
PWINDOW Wnd; //定义一窗口对象
Wnd = ValidateHwnd(hWnd); //验证窗口句柄是否合格
if (Wnd == NULL)
return 0;
if (nIndex >= 0)
{
if ((DWORD)nIndex + sizeof(LONG) > Wnd->ExtraDataSize) //这时取得是窗口扩展数据,并非类扩展数据
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
return *((LONG *)((PCHAR)(Wnd + 1) + nIndex)); //注意这里的计算,Wnd + 1其实就是将指针移到窗口对象之后,然后加上nIndex偏移
}
else
{
switch (nIndex)
{
case GWL_EXSTYLE: //扩展风格
return Wnd->ExStyle;
case GWL_STYLE:
return Wnd->Style; //普通风格
case GWL_HINSTANCE: //窗口模块句柄
return (LONG)Wnd->Instance;
case GWL_ID:
return Wnd->IDMenu; //
case GWL_USERDATA: //
return Wnd->UserData;
case GWL_HWNDPARENT: //父窗口
{
HWND parent = GetAncestor( hWnd, GA_PARENT );
if (parent == GetDesktopWindow()) parent = GetWindow( hWnd, GW_OWNER );
return (LONG)parent;
}
case GWL_WNDPROC: //回调函数
/* Call win32k for this as a callproc handle may need
to be created */
return NtUserGetWindowLong(hWnd, nIndex, FALSE);
default:
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
}
}
typedef struct _WINDOW
{
USER_OBJHDR hdr; /* FIXME: Move out of the structure once new handle manager is implemented */
/* NOTE: This structure is located in the desktop heap and will
eventually replace WINDOW_OBJECT. Right now WINDOW_OBJECT
keeps a reference to this structure until all the information
is moved to this structure */
struct _PROCESSINFO *pi; /* FIXME: Move to object header some day */ //三个进程指针,系统进程信息,W32进程信息,桌面进程信息
struct _W32THREADINFO *ti;
struct _DESKTOP *pdesktop;
RECT WindowRect;
RECT ClientRect;
WNDPROC WndProc; //回调函数
union
{
/* Pointer to a call procedure handle */
PCALLPROC CallProc;
/* Extra Wnd proc (windows of system classes) */
WNDPROC WndProcExtra;
};
struct _WINDOW *Parent; //父窗口
struct _WINDOW *Owner; //子窗口
/* Size of the extra data associated with the window. */
ULONG ExtraDataSize; //窗口扩展数据,紧跟窗口对象,并初始化为零
/* Style. */
DWORD Style;
/* Extended style. */
DWORD ExStyle;
/* Handle of the module that created the window. */
HINSTANCE Instance; //创建窗口的模块句柄,是创建者!
/* Window menu handle or window id */
UINT IDMenu; //菜单或者窗口的ID值
LONG UserData; //用户数据
/* Pointer to the window class. */
PWINDOWCLASS Class; //窗口类
/* Window name. */
UNICODE_STRING WindowName; //窗口名字
/* Context help id */
DWORD ContextHelpId; //HELP ID
HWND hWndLastActive; //最后一次活动窗口
/* Property list head.*/
LIST_ENTRY PropListHead; //属性链表
ULONG PropListItems;
struct
{
RECT NormalRect; //
POINT IconPos;
POINT MaxPos;
} InternalPos;
UINT Unicode : 1; //一些标志位,是否是UINICODE
/* Indicates whether the window is derived from a system class */
UINT IsSystem : 1; //是否从系统类继承下来的
UINT InternalPosInitialized : 1; //初始的位置
UINT HideFocus : 1; //隐藏焦点
UINT HideAccel : 1; //不接受快捷键
} WINDOW, *PWINDOW;
所以EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW( hwnd, 0 )首先是取得EDIT扩展数据.其设计也很简单,其结构如下:
typedef struct
{
BOOL is_unicode; /* how the control was created */
LPWSTR text; /* the actual contents of the control */ //文本内容,重要性不用说了
UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
UINT buffer_size; /* the size of the buffer in characters */
UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
HFONT font; /* NULL means standard system font */
INT x_offset; /* scroll offset for multi lines this is in pixels //X轴偏移
for single lines it's in characters */
INT line_height; /* height of a screen line in pixels */
INT char_width; /* average character width in pixels */
DWORD style; /* sane version of wnd->dwStyle */
WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
INT undo_insert_count; /* number of characters inserted in sequence */
UINT undo_position; /* character index of the insertion and deletion */
LPWSTR undo_text; /* deleted text */
UINT undo_buffer_size; /* size of the deleted text buffer */
INT selection_start; /* == selection_end if no selection */
INT selection_end; /* == current caret position */
WCHAR password_char; /* == 0 if no password char, and for multi line controls */
INT left_margin; /* in pixels */
INT right_margin; /* in pixels */
RECT format_rect; //相当重要,它决定了从那个地方开始画一行内容
INT text_width; /* width of the widest line in pixels for multi line controls
and just line width for single line controls */
INT region_posx; /* Position of cursor relative to region: */
INT region_posy; /* -1: to left, 0: within, 1: to right */
void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
INT line_count; /* number of lines */
INT y_offset; /* scroll offset in number of lines */
BOOL bCaptureState; /* flag indicating whether mouse was captured */
BOOL bEnableState; /* flag keeping the enable state */
HWND hwndSelf; /* the our window handle */ 句柄
HWND hwndParent; /* Handle of parent for sending EN_* messages. 父窗口句柄
Even if parent will change, EN_* messages
should be sent to the first parent. */
HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
/*
* only for multi line controls
*/
INT lock_count; /* amount of re-entries in the EditWndProc */
INT tabs_count;
LPINT tabs;
LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
HLOCAL hloc32W; /* our unicode local memory block */
HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
or EM_SETHANDLE */
/*
* IME Data
*/
UINT composition_len; /* length of composition, 0 == no composition */
int composition_start; /* the character position for the composition */
} EDITSTATE;
是在WM_CREATE进行初始化的:
case WM_CREATE:
if(unicode)
result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName)
再看wm_paint:
case WM_PRINTCLIENT:
case WM_PAINT:
EDIT_WM_Paint(es, (HDC)wParam);
break;
如下:
static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc)
{
PAINTSTRUCT ps;
INT i;
HDC dc;
HFONT old_font = 0;
RECT rc;
RECT rcClient;
RECT rcLine;
RECT rcRgn;
HBRUSH brush;
HBRUSH old_brush;
INT bw, bh;
BOOL rev = es->bEnableState &&
((es->flags & EF_FOCUSED) ||
(es->style & ES_NOHIDESEL));
dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps);
GetClientRect(es->hwndSelf, &rcClient);
/* get the background brush */
brush = EDIT_NotifyCtlColor(es, dc); //向父窗口发送取得背景颜色消息:WM_CTLCOLORSTATIC或WM_CTLCOLOREDIT
/* paint the border and the background */
IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);//设置描述符的RECT
if(es->style & WS_BORDER) {
bw = GetSystemMetrics(SM_CXBORDER);
bh = GetSystemMetrics(SM_CYBORDER);
rc = rcClient;
if(es->style & ES_MULTILINE) {
if(es->style & WS_HSCROLL) rc.bottom+=bh;
if(es->style & WS_VSCROLL) rc.right+=bw; //根据BORDER修改RECT大小
}
/* Draw the frame. Same code as in nonclient.c */
old_brush = SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME)); //设置边框颜色
PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY); //以下是画四条边框
PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY);
PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY);
PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY);
SelectObject(dc, old_brush);
/* Keep the border clean */
IntersectClipRect(dc, rc.left+bw, rc.top+bh, //再次将RECT恢复
max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh));
}
GetClipBox(dc, &rc);
FillRect(dc, &rc, brush);
IntersectClipRect(dc, es->format_rect.left, //设置可画的RECT
es->format_rect.top,
es->format_rect.right,
es->format_rect.bottom);
if (es->style & ES_MULTILINE) {
rc = rcClient;
IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
}
if (es->font)
old_font = SelectObject(dc, es->font);
if (!es->bEnableState)
SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
GetClipBox(dc, &rcRgn);
if (es->style & ES_MULTILINE) {
INT vlc = get_vertical_line_count(es);
for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) { //多行,在DELPHI里是TMEMO
EDIT_GetLineRect(es, i, 0, -1, &rcLine);
if (IntersectRect(&rc, &rcRgn, &rcLine))
EDIT_PaintLine(es, dc, i, rev); //画每一行内容
}
} else {
EDIT_GetLineRect(es, 0, 0, -1, &rcLine); //单行,DELPHI的EDIT
if (IntersectRect(&rc, &rcRgn, &rcLine))
EDIT_PaintLine(es, dc, 0, rev);
}
if (es->font) //恢复原有属性
SelectObject(dc, old_font);
if (!hdc)
EndPaint(es->hwndSelf, &ps);
}
//设置插入符
static void EDIT_WM_SetFocus(EDITSTATE *es)
{
es->flags |= EF_FOCUSED;
if (!(es->style & ES_NOHIDESEL))
EDIT_InvalidateText(es, es->selection_start, es->selection_end);
/* single line edit updates itself */如果是单行格式,就刷新下TEXT
if (!(es->style & ES_MULTILINE))
{
HDC hdc = GetDC(es->hwndSelf);
EDIT_WM_Paint(es, hdc);
ReleaseDC(es->hwndSelf, hdc);
}
CreateCaret(es->hwndSelf, 0, 1, es->line_height); //插入符本身也是一种用户对象
EDIT_SetCaretPos(es, es->selection_end,
es->flags & EF_AFTER_WRAP);
ShowCaret(es->hwndSelf); //直接调用系统服务NtUserShowCaret,系统负责控制插入符的显示
EDIT_NOTIFY_PARENT(es, EN_SETFOCUS);
}
//删除插入符
static LRESULT EDIT_WM_KillFocus(EDITSTATE *es)
{
es->flags &= ~EF_FOCUSED;
DestroyCaret();
if(!(es->style & ES_NOHIDESEL))
EDIT_InvalidateText(es, es->selection_start, es->selection_end);
EDIT_NOTIFY_PARENT(es, EN_KILLFOCUS);
return 0;
}
从Edit的WM_Paint处理例程来看,那么有这样的一个问题,就是单行的EDIT并不能设置Format_Rect,那么现在要求设置单行EDIT上下左右都居中,怎么办?
利用以上知识,我们知道,解决以上问题,只有设置Format_Rect一种方法(当然有些特殊的方法,基本上很麻烦,不算!),那就是在ES_MUTLILINE风格下可以设置,但是EDIT偏偏就是NOT ES_MUTLILINE风格.经过思考终于想到一个方案,如下:
重载Edit的CreateParams并设置其为ES_MUTLILINE风格
procedure TEdit.CreateParams(var Params: TCreateParams);
begin
inherited ;
Params.Style := Params.Style or ES_CENTER or ES_MULTILINE;
end;
//设置Format_Rect
procedure TForm1.FormCreate(Sender: TObject);
var
EditStyle: DWord;
EditRect: TRect;
begin
EditStyle := Windows.GetWindowLong(Edit1.Handle,GWL_STYLE);
if (EditStyle and ES_MULTILINE > 0) then
begin
//Get Edit FormatRect
Windows.SetRect(EditRect,0,0,0,0);
SendMessage(Edit1.Handle,EM_GETRECT,0,DWord(@EditRect));
//Set Edit FormatRect
OffsetRect(EditRect,10,10);
SendMessage(Edit1.Handle,EM_SETRECT,0,DWord(@EditRect));
end;
end;
//重载Edit的WM_CHAR消息,以使VK_RETURN不再处理换行作用,也就是相当于屏蔽了ES_MUTLILINE风格
procedure TEdit.WMChar(var Message: TWMChar);
begin
if Message.CharCode = VK_Return then
begin
//todo
end
else
inherited;
end;
----------Henry 20100705