组合框:顾名思义是几个窗体组合而成的.先看下Delphi组合框的TCOMBOBOX类:
TComboBox = class(TCustomComboBox) = class(TCustomCombo) = class(TCustomListControl) = class(TWinControl)
在TCustomComboBox之前的都是DELPHI一手捏造的,到了TCustomComboBox就子类化了,成为名符其实的WINDOW控件.
TCustomListControl这是一个纯虚类,它的存在仅仅是一种设计思想.TCustomCombo类其实也是虚类,它的存在的目的就是让DELPHI与WINDOW控件分离开来.其实只要大家注意,这个设计思想贯穿整个VCL,那么为什么要使用这个设计思想呢?因为WINDOWS是另外一家公司,他的变化不在BORLAND公司的控制之内,把不能控制的变化独立出来,把相同的独立出来大概是设计的核心吧.
限制全局的变化只在一个地方,限制不能控制的变化在最小区域.Delphi的VCL这点还是做得蛮好的.
既然是组合框,那到底是那个窗口组合的呢?答:是一个EDIT,一个LISTBOX,一个小下拉窗体,共三个窗口组合而成的.
其中,"小下拉窗体"是主窗体,是另外两个的父窗体,它的第一子窗体是EDIT,第二个子窗体是LISTBOX.我们看下DELPHI是如何取得两子窗体的.
procedure TCustomComboBox.CreateWnd;
begin
inherited CreateWnd;
FDropHandle := Handle;
....
ChildHandle := GetWindow(Handle, GW_CHILD);
...
FListHandle := GetWindow(ChildHandle, GW_HWNDNEXT); //大概是这样的
...
end;
下面具体看下Combobox类信息:
static const WCHAR comboboxW[] = {'C','o','m','b','o','B','o','x',0};
const struct builtin_class_descr COMBO_builtin_class =
{
comboboxW, /* name */
CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, /* style */ //是不是有点似曾相识感觉
ComboWndProcA, /* procA */
ComboWndProcW, /* procW */
sizeof(HEADCOMBO *), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
};
可以看combobox也有个窗体扩展数据结构:
/* combo state struct */
typedef struct
{
HWND self; //自身句柄
HWND owner; //拥有者句柄
UINT dwStyle; //类型
HWND hWndEdit; //EDIT句柄
HWND hWndLBox; //LIST句柄
UINT wState; //状态
HFONT hFont; //字体
RECT textRect; //EDIT的RECT
RECT buttonRect; //BUTTON的RECT
RECT droppedRect; //下拉窗口RECT
INT droppedIndex; //当前下拉项
INT fixedOwnerDrawHeight; //行高
INT droppedWidth; /* last two are not used unless set */ //行宽
INT editHeight; /* explicitly */ //行高
LONG UIState; //UI状态
} HEADCOMBO,*LPHEADCOMBO;
在WM_CREATE里调用的是:
lphc->hWndLBox = CreateWindowExA(lbeExStyle, "ComboLBox", NULL, lbeStyle,
lphc->droppedRect.left,
lphc->droppedRect.top,
lphc->droppedRect.right - lphc->droppedRect.left,
lphc->droppedRect.bottom - lphc->droppedRect.top,
hwnd, (HMENU)ID_CB_LISTBOX,
(HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc );
可见,LISTBOX并不真正WINDOWS的LISTBOX,而是专门的"ComboLBox"的类.
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
/* Now do the trick with parent */
SetParent(lphc->hWndLBox, HWND_DESKTOP);
...
如果不是CBS_SIMPLE类型的话,那个LBOX的父窗口是桌面.
lphc->hWndEdit = CreateWindowExA(0, "Edit", NULL, lbeStyle,
lphc->textRect.left, lphc->textRect.top,
lphc->textRect.right - lphc->textRect.left,
lphc->textRect.bottom - lphc->textRect.top,
hwnd, (HMENU)ID_CB_EDIT,
(HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL );
编辑框倒是WINDOWS通用的EDIT.
static LRESULT ComboWndProc_common( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam, BOOL unicode )
{
LPHEADCOMBO lphc = (LPHEADCOMBO)GetWindowLongPtrW( hwnd, 0 );
TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
hwnd, SPY_GetMsgName(message, hwnd), wParam, lParam );
if( lphc || message == WM_NCCREATE )
switch(message)
{
/* System messages */
case WM_NCCREATE: //非客户区创建消息
{
LONG style = unicode ? ((LPCREATESTRUCTW)lParam)->style :
((LPCREATESTRUCTA)lParam)->style;
return COMBO_NCCreate(hwnd, style);
}
case WM_NCDESTROY: //非客户区释放消息
COMBO_NCDestroy(lphc);
break;/* -> DefWindowProc */
case WM_CREATE: //创建初始化消息
{
HWND hwndParent;
LONG style;
if(unicode)
{
hwndParent = ((LPCREATESTRUCTW)lParam)->hwndParent;
style = ((LPCREATESTRUCTW)lParam)->style;
}
else
{
hwndParent = ((LPCREATESTRUCTA)lParam)->hwndParent;
style = ((LPCREATESTRUCTA)lParam)->style;
}
return COMBO_Create(hwnd, lphc, hwndParent, style, unicode); //,就是在此创建的EDIT与COMBOLBOXR的.
}
case WM_PRINTCLIENT:
/* Fallthrough */
case WM_PAINT:
/* wParam may contain a valid HDC! */
return COMBO_Paint(lphc, (HDC)wParam); //画图函数,因为是多窗体,所以要画三个窗口
case WM_ERASEBKGND: //擦除背景已经在WM_PAINT里做好了,所以不用了
/* do all painting in WM_PAINT like Windows does */
return 1;
case WM_GETDLGCODE: //接收键盘的按键
{
LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS; //方向键和字符键
if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN)) //如果是WM_KEYDOWN消息发出的消息
{
int vk = (int)((LPMSG)lParam)->wParam;
if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED))
result |= DLGC_WANTMESSAGE;
}
return result;
}
case WM_WINDOWPOSCHANGING: //正在修改位置,发送WM_SIZING消息
return COMBO_WindowPosChanging(hwnd, lphc, (LPWINDOWPOS)lParam);
case WM_WINDOWPOSCHANGED: //修改完位置,发送WM_SIZE消息
/* SetWindowPos can be called on a Combobox to resize its Listbox.
* In that case, the Combobox itself will not be resized, so we won't
* get a WM_SIZE. Since we still want to update the Listbox, we have to
* do it here.
*/
/* we should not force repainting on WM_WINDOWPOSCHANGED, it breaks
* Z-order based painting.
*/
/* fall through */
case WM_SIZE: //不用说了,对COMBOBOX本身来说,因为大小不能改变,所以WM_SIZE已经失去了意义,但是对于COMBOLBOX来说,WM_SIZE是有意义的,在SIMPLI情况下.
if( lphc->hWndLBox &&
!(lphc->wState & CBF_NORESIZE) ) COMBO_Size( lphc, message == WM_SIZE );
return TRUE;
case WM_SETFONT: //设置字体
COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam );
return TRUE;
case WM_GETFONT: //取得字体
return (LRESULT)lphc->hFont;
case WM_SETFOCUS: //设置焦点
if( lphc->wState & CBF_EDIT )
SetFocus( lphc->hWndEdit );
else
COMBO_SetFocus( lphc );
return TRUE;
case WM_KILLFOCUS: //失去焦点
{
HWND hwndFocus = WIN_GetFullHandle( (HWND)wParam );
if( !hwndFocus ||
(hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox ))
COMBO_KillFocus( lphc ); //如果在COMBOLBOX上,则关闭COMBOLBOX
return TRUE;
}
case WM_COMMAND: //向父窗口发送命令消息
return COMBO_Command( lphc, wParam, WIN_GetFullHandle( (HWND)lParam ) );//
case WM_GETTEXT: //取得TEXT
return unicode ? COMBO_GetTextW( lphc, wParam, (LPWSTR)lParam )
: COMBO_GetTextA( lphc, wParam, (LPSTR)lParam );
case WM_SETTEXT:
case WM_GETTEXTLENGTH:
case WM_CLEAR: //...
if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT))
{
int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
if (j == -1) return 0;
return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0) :
SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, j, 0);
}
else if( lphc->wState & CBF_EDIT )
{
LRESULT ret;
lphc->wState |= CBF_NOEDITNOTIFY;
ret = unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
SendMessageA(lphc->hWndEdit, message, wParam, lParam);
lphc->wState &= ~CBF_NOEDITNOTIFY;
return ret;
}
else return CB_ERR;
case WM_CUT:
case WM_PASTE:
case WM_COPY: //剪贴板三消息合并在一起
if( lphc->wState & CBF_EDIT )
{
return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
SendMessageA(lphc->hWndEdit, message, wParam, lParam);
}
else return CB_ERR;
case WM_DRAWITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
case WM_MEASUREITEM: //确定项数据,注意和LIST比较下,有点不同.
return COMBO_ItemOp(lphc, message, lParam);
case WM_ENABLE: //可用处理消息
if( lphc->wState & CBF_EDIT )
EnableWindow( lphc->hWndEdit, (BOOL)wParam );
EnableWindow( lphc->hWndLBox, (BOOL)wParam );
/* Force the control to repaint when the enabled state changes. */
InvalidateRect(lphc->self, NULL, TRUE);
return TRUE;
case WM_SETREDRAW: //重画消息
if( wParam )
lphc->wState &= ~CBF_NOREDRAW;
else
lphc->wState |= CBF_NOREDRAW;
if( lphc->wState & CBF_EDIT )
SendMessageW(lphc->hWndEdit, message, wParam, lParam);
SendMessageW(lphc->hWndLBox, message, wParam, lParam);
return 0;
case WM_SYSKEYDOWN: //系统按键
if( KEYDATA_ALT & HIWORD(lParam) )
if( wParam == VK_UP || wParam == VK_DOWN )
COMBO_FlipListbox( lphc, FALSE, FALSE );
return 0;
case WM_CHAR:
case WM_IME_CHAR:
case WM_KEYDOWN:
{
HWND hwndTarget;
if ((wParam == VK_RETURN || wParam == VK_ESCAPE) &&
(lphc->wState & CBF_DROPPED))
{
CBRollUp( lphc, wParam == VK_RETURN, FALSE );
return TRUE;
}
else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI))
{
COMBO_FlipListbox( lphc, FALSE, FALSE );
return TRUE;
}
if( lphc->wState & CBF_EDIT )
hwndTarget = lphc->hWndEdit;
else
hwndTarget = lphc->hWndLBox;
return unicode ? SendMessageW(hwndTarget, message, wParam, lParam) :
SendMessageA(hwndTarget, message, wParam, lParam);
}
case WM_LBUTTONDOWN:
if( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self );
if( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam );
return TRUE;
case WM_LBUTTONUP:
COMBO_LButtonUp( lphc );
return TRUE;
case WM_MOUSEMOVE:
if( lphc->wState & CBF_CAPTURE )
COMBO_MouseMove( lphc, wParam, lParam );
return TRUE;
case WM_MOUSEWHEEL:
if (wParam & (MK_SHIFT | MK_CONTROL))
return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
DefWindowProcA(hwnd, message, wParam, lParam);
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0);
if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
return TRUE;
/* Combo messages */自己单独的消息
case CB_ADDSTRING: //添加字符串
if( unicode )
{
if( lphc->dwStyle & CBS_LOWERCASE )
CharLowerW((LPWSTR)lParam);
else if( lphc->dwStyle & CBS_UPPERCASE )
CharUpperW((LPWSTR)lParam);
return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam);
}
else /* unlike the unicode version, the ansi version does not overwrite
the string if converting case */
{
char *string = NULL;
LRESULT ret;
if( lphc->dwStyle & CBS_LOWERCASE )
{
string = strdupA((LPSTR)lParam);
CharLowerA(string);
}
else if( lphc->dwStyle & CBS_UPPERCASE )
{
string = strdupA((LPSTR)lParam);
CharUpperA(string);
}
ret = SendMessageA(lphc->hWndLBox, LB_ADDSTRING, 0, string ? (LPARAM)string : lParam);
HeapFree(GetProcessHeap(), 0, string);
return ret;
}
case CB_INSERTSTRING: //插入字符串
if( unicode )
{
if( lphc->dwStyle & CBS_LOWERCASE )
CharLowerW((LPWSTR)lParam);
else if( lphc->dwStyle & CBS_UPPERCASE )
CharUpperW((LPWSTR)lParam);
return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
}
else
{
if( lphc->dwStyle & CBS_LOWERCASE )
CharLowerA((LPSTR)lParam);
else if( lphc->dwStyle & CBS_UPPERCASE )
CharUpperA((LPSTR)lParam);
return SendMessageA(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam);
}
case CB_DELETESTRING: //删除字符串
return unicode ? SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0) :
SendMessageA(lphc->hWndLBox, LB_DELETESTRING, wParam, 0);
case CB_SELECTSTRING: //选中字符串
return COMBO_SelectString(lphc, (INT)wParam, lParam, unicode);
case CB_FINDSTRING: //查找字符串
return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam) :
SendMessageA(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam);
case CB_FINDSTRINGEXACT: //扩展查找字符串
return unicode ? SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam) :
SendMessageA(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam);
case CB_SETITEMHEIGHT: //设置项高
return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam);
case CB_GETITEMHEIGHT: //取项高
if( (INT)wParam >= 0 ) /* listbox item */
return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
return CBGetTextAreaHeight(hwnd, lphc);
case CB_RESETCONTENT: //重置内容
SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
if( (lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc) )
{
static const WCHAR empty_stringW[] = { 0 };
SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW);
}
else
InvalidateRect(lphc->self, NULL, TRUE);
return TRUE;
case CB_INITSTORAGE: //初始化存储
return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam);
case CB_GETHORIZONTALEXTENT:
return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0);
case CB_SETHORIZONTALEXTENT:
return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0);
case CB_GETTOPINDEX: //当前页首行
return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0);
case CB_GETLOCALE:
return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0);
case CB_SETLOCALE:
return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0);
case CB_GETDROPPEDWIDTH:
if( lphc->droppedWidth )
return lphc->droppedWidth;
return lphc->droppedRect.right - lphc->droppedRect.left;
case CB_SETDROPPEDWIDTH:
if( (CB_GETTYPE(lphc) != CBS_SIMPLE) &&
(INT)wParam < 32768 ) lphc->droppedWidth = (INT)wParam;
return CB_ERR;
case CB_GETDROPPEDCONTROLRECT:
if( lParam ) CBGetDroppedControlRect(lphc, (LPRECT)lParam );
return CB_OKAY;
case CB_GETDROPPEDSTATE:
return (lphc->wState & CBF_DROPPED) ? TRUE : FALSE;
case CB_DIR:
return unicode ? SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam) :
SendMessageA(lphc->hWndLBox, LB_DIR, wParam, lParam);
case CB_SHOWDROPDOWN: //显示下拉窗口
if( CB_GETTYPE(lphc) != CBS_SIMPLE )
{
if( wParam )
{
if( !(lphc->wState & CBF_DROPPED) )
CBDropDown( lphc );
}
else
if( lphc->wState & CBF_DROPPED )
CBRollUp( lphc, FALSE, TRUE );
}
return TRUE;
case CB_GETCOUNT:
return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0);
case CB_GETCURSEL:
return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
case CB_SETCURSEL:
lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0);
if( lParam >= 0 )
SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0);
/* no LBN_SELCHANGE in this case, update manually */
if( lphc->wState & CBF_EDIT )
CBUpdateEdit( lphc, (INT)wParam );
else
InvalidateRect(lphc->self, &lphc->textRect, TRUE);
lphc->wState &= ~CBF_SELCHANGE;
return lParam;
case CB_GETLBTEXT:
return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam) :
SendMessageA(lphc->hWndLBox, LB_GETTEXT, wParam, lParam);
case CB_GETLBTEXTLEN:
return unicode ? SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0) :
SendMessageA(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0);
case CB_GETITEMDATA:
return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0);
case CB_SETITEMDATA:
return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam);
case CB_GETEDITSEL:
/* Edit checks passed parameters itself */
if( lphc->wState & CBF_EDIT )
return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam);
return CB_ERR;
case CB_SETEDITSEL:
if( lphc->wState & CBF_EDIT )
return SendMessageW(lphc->hWndEdit, EM_SETSEL,
(INT)(INT16)LOWORD(lParam), (INT)(INT16)HIWORD(lParam) );
return CB_ERR;
case CB_SETEXTENDEDUI:
if( CB_GETTYPE(lphc) == CBS_SIMPLE )
return CB_ERR;
if( wParam )
lphc->wState |= CBF_EUI;
else lphc->wState &= ~CBF_EUI;
return CB_OKAY;
case CB_GETEXTENDEDUI:
return (lphc->wState & CBF_EUI) ? TRUE : FALSE;
case CB_GETCOMBOBOXINFO:
return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam);
case CB_LIMITTEXT:
if( lphc->wState & CBF_EDIT )
return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam);
case WM_UPDATEUISTATE: //更新UI
if (unicode)
DefWindowProcW(lphc->self, message, wParam, lParam);
else
DefWindowProcA(lphc->self, message, wParam, lParam);
if (COMBO_update_uistate(lphc))
{
/* redraw text */
if( !(lphc->wState & CBF_EDIT) )
NtUserInvalidateRect(lphc->self, &lphc->textRect, TRUE);
}
break;
default:
if (message >= WM_USER)
WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n",
message - WM_USER, wParam, lParam );
break;
}
return unicode ? DefWindowProcW(hwnd, message, wParam, lParam) :
DefWindowProcA(hwnd, message, wParam, lParam);
}
COMBO_Paint函数主要画EDIT内容的,如果是OWNER_DRAW则发送DRAWITEM消息,否则调用EXTTEXTOUT输出字符.并且画按钮调用:
/***********************************************************************
* CBPaintButton
*
* Paint combo button (normal, pressed, and disabled states).
*/
static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
{
UINT buttonState = DFCS_SCROLLCOMBOBOX;
if( lphc->wState & CBF_NOREDRAW )
return;
if (lphc->wState & CBF_BUTTONDOWN)
buttonState |= DFCS_PUSHED;
if (CB_DISABLED(lphc))
buttonState |= DFCS_INACTIVE;
DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState); //注意在WINODWS内部经常调用此函数
}
BOOL DrawFrameControl(
HDC hdc, // handle to device context
LPRECT lprc, // bounding rectangle
UINT uType, // frame-control type
UINT uState // frame-control state
)
这里WINDOWS提供画各种控件的通用函数.比如:
procedure TForm1.Button1Click(Sender: TObject);
var
ARect: TRect;
begin
ARect := Bounds(100,100,50,50);
DrawFrameControl(Canvas.Handle,ARect,DFC_CAPTION,DFCS_HOT or DFCS_FLAT)
end;
COMBO_Command主是处理EDIT和COMBOLBOX的通知消息.
再看如何取得GETTEXT消息:
static LRESULT COMBO_GetTextW( LPHEADCOMBO lphc, INT count, LPWSTR buf )
{
INT length;
if( lphc->wState & CBF_EDIT )
return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf ); //直接取EDIT的TEXT
/* get it from the listbox */
if (!count || !buf) return 0; //如果不EDIT风格,则从COMBOLBOX里取数据
if( lphc->hWndLBox )
{
INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0);
if (idx == LB_ERR) goto error;
length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 );
if (length == LB_ERR) goto error;
/* 'length' is without the terminating character */
if (length >= count)
{
LPWSTR lpBuffer = HeapAlloc(GetProcessHeap(), 0, (length + 1) * sizeof(WCHAR));
if (!lpBuffer) goto error;
length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer);
/* truncate if buffer is too short */
if (length != LB_ERR)
{
lstrcpynW( buf, lpBuffer, count );
length = count;
}
HeapFree( GetProcessHeap(), 0, lpBuffer );
}
else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf);
if (length == LB_ERR) return 0;
return length;
}
error: /* error - truncate string, return zero */
buf[0] = 0;
return 0;
}
再看剪贴板消息,他直接把它传送给EDIT.
case WM_CUT:
case WM_PASTE:
case WM_COPY:
if( lphc->wState & CBF_EDIT )
{
return unicode ? SendMessageW(lphc->hWndEdit, message, wParam, lParam) :
SendMessageA(lphc->hWndEdit, message, wParam, lParam);
}
else return CB_ERR;
再看WM_SYSKEYDOWN:
BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton )
{
if( lphc->wState & CBF_DROPPED ) 如果弹出下拉窗口时,则关闭
{
CBRollUp( lphc, ok, bRedrawButton );
return FALSE;
}
CBDropDown( lphc ); //如果没有弹出,则弹出
return TRUE;
}
弹出下拉窗口
static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton )
{
HWND hWnd = lphc->self;
TRACE("[%p]: sel ok? [%i] dropped? [%i]\n",
lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED));
CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL ); //通知选中某项
if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE ) //不是CBS_SIMPLE风格的话
{
if( lphc->wState & CBF_DROPPED )
{
RECT rect;
lphc->wState &= ~CBF_DROPPED;
ShowWindow( lphc->hWndLBox, SW_HIDE );
if(GetCapture() == lphc->hWndLBox)
{
ReleaseCapture(); //释放鼠标
}
if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
{
rect = lphc->buttonRect;
}
else
{
if( bButton )
{
UnionRect( &rect, //计算下拉窗口RECT
&lphc->buttonRect,
&lphc->textRect);
}
else
rect = lphc->textRect;
bButton = TRUE;
}
if( bButton && !(lphc->wState & CBF_NOREDRAW) )
RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | //画下拉窗口
RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN );
CB_NOTIFY( lphc, CBN_CLOSEUP ); //通知COMBOBOX已经下拉
}
}
}
static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam )
{
HWND hWnd = lphc->self;
UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID );
TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg );
switch( msg )
{
case WM_DELETEITEM: //删除某项
{
DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam;
lpIS->CtlType = ODT_COMBOBOX;
lpIS->CtlID = id;
lpIS->hwndItem = hWnd;
break;
}
case WM_DRAWITEM: //画某项
{
DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam;
lpIS->CtlType = ODT_COMBOBOX;
lpIS->CtlID = id;
lpIS->hwndItem = hWnd;
break;
}
case WM_COMPAREITEM: //比如某项数据
{
COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam;
lpIS->CtlType = ODT_COMBOBOX;
lpIS->CtlID = id;
lpIS->hwndItem = hWnd;
break;
}
case WM_MEASUREITEM: //画之前确定某项数据
{
MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam;
lpIS->CtlType = ODT_COMBOBOX;
lpIS->CtlID = id;
break;
}
}
return SendMessageW(lphc->owner, msg, id, lParam);//注意这里,直接上传给COMBOBOX
}
----------Henry 20100711