TListBox其实是一个相对简单的控件,但是他又是一个在DELPHI中非常有艺术的类.他的继承关系如下:
TListBox = class(TCustomListBox) = class(TCustomMultiSelectListControl) = class(TCustomListControl) = class(TWinControl)
其实主要实现还是TCustomListBox,这个类子类化了WINDOWS的ListBox控件.
其实在WINODWS里有两个ListBox,一个是提供用户使用的"ListBox",另一个是提供给Combobox使用的"ComboLBox".
/*********************************************************************
* listbox class descriptor
*/
static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
const struct builtin_class_descr LISTBOX_builtin_class =
{
listboxW, /* name */
CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
ListBoxWndProcA, /* procA */
ListBoxWndProcW, /* procW */
sizeof(LB_DESCR *), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
};
/*********************************************************************
* combolbox class descriptor
*/
static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
const struct builtin_class_descr COMBOLBOX_builtin_class =
{
combolboxW, /* name */
CS_DBLCLKS | CS_SAVEBITS, /* style */
ListBoxWndProcA, /* procA */
ListBoxWndProcW, /* procW */
sizeof(LB_DESCR *), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
};
他们都有一个窗口扩展数据结构:
/* Item structure */
typedef struct //项数据结构
{
LPWSTR str; /* Item text */ //字符串
BOOL selected; /* Is item selected? */ //是否选中
UINT height; /* Item height (only for OWNERDRAWVARIABLE) */ //高
ULONG_PTR data; /* User data */ //用户数据
} LB_ITEMDATA;
/* Listbox structure */
typedef struct
{
HWND self; /* Our own window handle */
HWND owner; /* Owner window to send notifications to */
UINT style; /* Window style */
INT width; /* Window width */
INT height; /* Window height */
LB_ITEMDATA *items; /* Array of items */ //以数组的形式存放所有的项数据
INT nb_items; /* Number of items */
INT top_item; /* Top visible item */
INT selected_item; /* Selected item */ //选中项
INT focus_item; /* Item that has the focus */ //焦点项
INT anchor_item; /* Anchor item for extended selection */ //瞄准项
INT item_height; /* Default item height */
INT page_size; /* Items per listbox page */ //每页项数
INT column_width; /* Column width for multi-column listboxes */
INT horz_extent; /* Horizontal extent (0 if no hscroll) */
INT horz_pos; /* Horizontal position */
INT nb_tabs; /* Number of tabs in array */
INT *tabs; /* Array of tabs */
INT avg_char_width; /* Average width of characters */
BOOL caret_on; /* Is caret on? */
BOOL captured; /* Is mouse captured? */
BOOL in_focus;
HFONT font; /* Current font */
LCID locale; /* Current locale for string comparisons */
LPHEADCOMBO lphc; /* ComboLBox */
LONG UIState;
} LB_DESCR;
下面看下LISTBOX的窗口过程:
static LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam, BOOL unicode )
{
LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
LPHEADCOMBO lphc = 0;
LRESULT ret;
if (!descr)
{
if (!IsWindow(hwnd)) return 0;
if (msg == WM_CREATE)
{
CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
if (!LISTBOX_Create( hwnd, lphc )) return -1;
TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
return 0;
}
/* Ignore all other messages before we get a WM_CREATE */
return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
DefWindowProcA( hwnd, msg, wParam, lParam );
}
if (descr->style & LBS_COMBOBOX) lphc = descr->lphc; //首先取得头数据,状态数据
TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
switch(msg)
{
case LB_RESETCONTENT: //重置内容
LISTBOX_ResetContent( descr );
LISTBOX_UpdateScroll( descr );
InvalidateRect( descr->self, NULL, TRUE );
return 0;
case LB_ADDSTRING:
case LB_ADDSTRING_LOWER:
case LB_ADDSTRING_UPPER: //添加项(含大小写处理)
{
INT ret;
LPWSTR textW;
if(unicode || !HAS_STRINGS(descr))
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);
else
return LB_ERRSPACE;
}
/* in the unicode the version, the string is really overwritten
during the converting case */
if (msg == LB_ADDSTRING_LOWER)
strlwrW(textW);
else if (msg == LB_ADDSTRING_UPPER)
struprW(textW);
wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
ret = LISTBOX_InsertString( descr, wParam, textW );
if (!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_INSERTSTRING:
case LB_INSERTSTRING_UPPER:
case LB_INSERTSTRING_LOWER: //插入项(含大小写处理)
{
INT ret;
LPWSTR textW;
if(unicode || !HAS_STRINGS(descr))
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);
else
return LB_ERRSPACE;
}
/* in the unicode the version, the string is really overwritten
during the converting case */
if (msg == LB_INSERTSTRING_LOWER)
strlwrW(textW);
else if (msg == LB_INSERTSTRING_UPPER)
struprW(textW);
ret = LISTBOX_InsertString( descr, wParam, textW );
if(!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_ADDFILE: //加载FILE
{
INT ret;
LPWSTR textW;
if(unicode || !HAS_STRINGS(descr))
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);
else
return LB_ERRSPACE;
}
wParam = LISTBOX_FindFileStrPos( descr, textW );
ret = LISTBOX_InsertString( descr, wParam, textW );
if(!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_DELETESTRING: //删除项
if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
return descr->nb_items;
else
{
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
case LB_GETITEMDATA: //取得某项的用户数据
if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
{
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
return descr->items[wParam].data;
case LB_SETITEMDATA: //设置某项的用户数据
if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
{
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
descr->items[wParam].data = lParam;
/* undocumented: returns TRUE, not LB_OKAY (0) */
return TRUE;
case LB_GETCOUNT: //取得项数
return descr->nb_items;
case LB_GETTEXT: //取得某项的TEXT
return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
case LB_GETTEXTLEN: //取得某项TEXT的长度
if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
{
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
if (!HAS_STRINGS(descr)) return sizeof(DWORD);
if (unicode) return strlenW( descr->items[wParam].str );
return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
case LB_GETCURSEL: //取得当前选中的项
if (descr->nb_items == 0)
return LB_ERR;
if (!IS_MULTISELECT(descr))
return descr->selected_item;
if (descr->selected_item != -1)
return descr->selected_item;
return descr->focus_item;
/* otherwise, if the user tries to move the selection with the */
/* arrow keys, we will give the application something to choke on */
case LB_GETTOPINDEX: //取得TOP项
return descr->top_item;
case LB_GETITEMHEIGHT: //项高
return LISTBOX_GetItemHeight( descr, wParam );
case LB_SETITEMHEIGHT: //设置项高
return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
case LB_ITEMFROMPOINT: //取得某项从某位置
{
POINT pt;
RECT rect;
int index;
BOOL hit = TRUE;
/* The hiword of the return value is not a client area
hittest as suggested by MSDN, but rather a hittest on
the returned listbox item. */
if(descr->nb_items == 0)
return 0x1ffff; /* win9x returns 0x10000, we copy winnt */
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
SetRect(&rect, 0, 0, descr->width, descr->height);
if(!PtInRect(&rect, pt))
{
pt.x = min(pt.x, rect.right - 1);
pt.x = max(pt.x, 0);
pt.y = min(pt.y, rect.bottom - 1);
pt.y = max(pt.y, 0);
hit = FALSE;
}
index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
if(index == -1)
{
index = descr->nb_items - 1;
hit = FALSE;
}
return MAKELONG(index, hit ? 0 : 1);
}
case LB_SETCARETINDEX: //设置插入符
if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
return LB_ERR;
else if (ISWIN31)
return wParam;
else
return LB_OKAY;
case LB_GETCARETINDEX: //取得焦点项,即就是含有插入符的项
return descr->focus_item;
case LB_SETTOPINDEX: //取得当前页顶项
return LISTBOX_SetTopItem( descr, wParam, TRUE );
case LB_SETCOLUMNWIDTH: //列宽
return LISTBOX_SetColumnWidth( descr, wParam );
case LB_GETITEMRECT: //取得某项的RECT
return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
case LB_FINDSTRING: //查找字符串
{
INT ret;
LPWSTR textW;
if(unicode || !HAS_STRINGS(descr))
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);
}
ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
if(!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_FINDSTRINGEXACT: //扩展查找字符串
{
INT ret;
LPWSTR textW;
if(unicode || !HAS_STRINGS(descr))
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);
}
ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
if(!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_SELECTSTRING: //选中的字符串
{
INT index;
LPWSTR textW;
if(HAS_STRINGS(descr))
TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
debugstr_a((LPSTR)lParam));
if(unicode || !HAS_STRINGS(descr))
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);
}
index = LISTBOX_FindString( descr, wParam, textW, FALSE );
if(!unicode && HAS_STRINGS(descr))
HeapFree(GetProcessHeap(), 0, textW);
if (index != LB_ERR)
{
LISTBOX_MoveCaret( descr, index, TRUE );
LISTBOX_SetSelection( descr, index, TRUE, FALSE );
}
return index;
}
case LB_GETSEL: //取得选中项
if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
return LB_ERR;
return descr->items[wParam].selected;
case LB_SETSEL: //设置选中项
return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
case LB_SETCURSEL: //设置当前选中项内容
if (IS_MULTISELECT(descr)) return LB_ERR;
LISTBOX_SetCaretIndex( descr, wParam, FALSE );
ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
if (ret != LB_ERR) ret = descr->selected_item;
return ret;
case LB_GETSELCOUNT: //取得当前选中总项数
return LISTBOX_GetSelCount( descr );
case LB_GETSELITEMS: //取得当前选中总项,多选
return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
case LB_SELITEMRANGE: //取得当前选中区域
if (LOWORD(lParam) <= HIWORD(lParam))
return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
HIWORD(lParam), wParam );
else
return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
LOWORD(lParam), wParam );
case LB_SELITEMRANGEEX: //取得当前选中区域,扩展
if ((INT)lParam >= (INT)wParam)
return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
else
return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
case LB_GETHORIZONTALEXTENT: //取得水平超出宽度
return descr->horz_extent;
case LB_SETHORIZONTALEXTENT: //设置水平超出宽度
return LISTBOX_SetHorizontalExtent( descr, wParam );
case LB_GETANCHORINDEX: //设置水平超出项
return descr->anchor_item;
case LB_SETANCHORINDEX:
if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
{
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
descr->anchor_item = (INT)wParam;
return LB_OKAY;
case LB_DIR: //目录
{
INT ret;
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);
}
ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
if(!unicode)
HeapFree(GetProcessHeap(), 0, textW);
return ret;
}
case LB_GETLOCALE: //语言相关
return descr->locale;
case LB_SETLOCALE:
{
LCID ret;
if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
return LB_ERR;
ret = descr->locale;
descr->locale = (LCID)wParam;
return ret;
}
case LB_INITSTORAGE:
return LISTBOX_InitStorage( descr, wParam );
case LB_SETCOUNT: //设置总项数
return LISTBOX_SetCount( descr, (INT)wParam );
case LB_SETTABSTOPS:
return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam, FALSE );
case LB_CARETON: //打开插入符
if (descr->caret_on)
return LB_OKAY;
descr->caret_on = TRUE;
if ((descr->focus_item != -1) && (descr->in_focus))
LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
return LB_OKAY;
case LB_CARETOFF: //关闭插入符
if (!descr->caret_on)
return LB_OKAY;
descr->caret_on = FALSE;
if ((descr->focus_item != -1) && (descr->in_focus))
LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
return LB_OKAY;
case LB_GETLISTBOXINFO: //取得相关信息
FIXME("LB_GETLISTBOXINFO: stub!\n");
return 0;
case WM_DESTROY:
return LISTBOX_Destroy( descr );
case WM_ENABLE: //ENABLE
InvalidateRect( descr->self, NULL, TRUE );
return 0;
case WM_SETREDRAW: //重画
LISTBOX_SetRedraw( descr, wParam != 0 );
return 0;
case WM_GETDLGCODE: //接收方向键及字符
return DLGC_WANTARROWS | DLGC_WANTCHARS;
case WM_PRINTCLIENT:
case WM_PAINT: //这个不用说
{
PAINTSTRUCT ps;
HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps );
ret = LISTBOX_Paint( descr, hdc );
if( !wParam ) EndPaint( descr->self, &ps );
}
return ret;
case WM_SIZE: //不用说
LISTBOX_UpdateSize( descr );
return 0;
case WM_GETFONT: //取得字体
return (LRESULT)descr->font;
case WM_SETFONT: //设置字体
LISTBOX_SetFont( descr, (HFONT)wParam );
if (lParam) InvalidateRect( descr->self, 0, TRUE );
return 0;
case WM_SETFOCUS: //设置焦点
descr->in_focus = TRUE;
descr->caret_on = TRUE;
if (descr->focus_item != -1)
LISTBOX_DrawFocusRect( descr, TRUE );
SEND_NOTIFICATION( descr, LBN_SETFOCUS );
return 0;
case WM_KILLFOCUS: //失去焦点
descr->in_focus = FALSE;
if ((descr->focus_item != -1) && descr->caret_on)
LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
return 0;
case WM_HSCROLL: //水平滚动
return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
case WM_VSCROLL: //垂直滚动
return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
case WM_MOUSEWHEEL: //鼠标中键
if (wParam & (MK_SHIFT | MK_CONTROL))
return DefWindowProcW( descr->self, msg, wParam, lParam );
return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
case WM_LBUTTONDOWN: //鼠标单击
if (lphc)
return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
(INT16)LOWORD(lParam),
(INT16)HIWORD(lParam) );
return LISTBOX_HandleLButtonDown( descr, wParam,
(INT16)LOWORD(lParam),
(INT16)HIWORD(lParam) );
case WM_LBUTTONDBLCLK: //鼠标双击
if (lphc)
return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
(INT16)LOWORD(lParam),
(INT16)HIWORD(lParam) );
if (descr->style & LBS_NOTIFY)
SEND_NOTIFICATION( descr, LBN_DBLCLK );
return 0;
case WM_MOUSEMOVE: //鼠标滑轮
if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
{
BOOL captured = descr->captured;
POINT mousePos;
RECT clientRect;
mousePos.x = (INT16)LOWORD(lParam);
mousePos.y = (INT16)HIWORD(lParam);
/*
* If we are in a dropdown combobox, we simulate that
* the mouse is captured to show the tracking of the item.
*/
if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
descr->captured = TRUE;
LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
descr->captured = captured;
}
else if (GetCapture() == descr->self)
{
LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
(INT16)HIWORD(lParam) );
}
return 0;
case WM_LBUTTONUP: //鼠标按起
if (lphc)
{
POINT mousePos;
RECT clientRect;
/*
* If the mouse button "up" is not in the listbox,
* we make sure there is no selection by re-selecting the
* item that was selected when the listbox was made visible.
*/
mousePos.x = (INT16)LOWORD(lParam);
mousePos.y = (INT16)HIWORD(lParam);
GetClientRect(descr->self, &clientRect);
/*
* When the user clicks outside the combobox and the focus
* is lost, the owning combobox will send a fake buttonup with
* 0xFFFFFFF as the mouse location, we must also revert the
* selection to the original selection.
*/
if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
}
return LISTBOX_HandleLButtonUp( descr );
case WM_KEYDOWN: //键盘按下
if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
{
/* for some reason Windows makes it possible to
* show/hide ComboLBox by sending it WM_KEYDOWNs */
if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
&& (wParam == VK_DOWN || wParam == VK_UP)) )
{
COMBO_FlipListbox( lphc, FALSE, FALSE );
return 0;
}
}
return LISTBOX_HandleKeyDown( descr, wParam );
case WM_CHAR: //字符键
{
WCHAR charW;
if(unicode)
charW = (WCHAR)wParam;
else
{
CHAR charA = (CHAR)wParam;
MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
}
return LISTBOX_HandleChar( descr, charW );
}
case WM_SYSTIMER: //时钟
return LISTBOX_HandleSystemTimer( descr );
case WM_ERASEBKGND: //擦除背景
if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
{
RECT rect;
HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
wParam, (LPARAM)descr->self );
TRACE("hbrush = %p\n", hbrush);
if(!hbrush)
hbrush = GetSysColorBrush(COLOR_WINDOW);
if(hbrush)
{
GetClientRect(descr->self, &rect);
FillRect((HDC)wParam, &rect, hbrush);
}
}
return 1;
case WM_DROPFILES: //除去文件
if( lphc ) return 0;
return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
SendMessageA( descr->owner, msg, wParam, lParam );
case WM_NCDESTROY: //非客户区释放
if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
lphc->hWndLBox = 0;
break;
case WM_NCACTIVATE: //非客户区激活
if (lphc) return 0;
break;
case WM_UPDATEUISTATE: //更新UI
if (unicode)
DefWindowProcW(descr->self, msg, wParam, lParam);
else
DefWindowProcA(descr->self, msg, wParam, lParam);
if (LISTBOX_update_uistate(descr))
{
/* redraw text */
if (descr->focus_item != -1)
LISTBOX_DrawFocusRect( descr, descr->in_focus );
}
break;
default:
if ((msg >= WM_USER) && (msg < 0xc000))
WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
hwnd, msg, wParam, lParam );
}
return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
DefWindowProcA( hwnd, msg, wParam, lParam );
}
在DELPHI里用一个TListBoxStrings = class(TStrings)将ListBox中ItemS项的TEXT和DATA整合到一块,非常巧妙!
举一例子:
function TListBoxStrings.Get(Index: Integer): string;
var
Len: Integer;
begin
if ListBox.Style in [lbVirtual, lbVirtualOwnerDraw] then
Result := ListBox.DoGetData(Index)
else
begin
Len := SendMessage(ListBox.Handle, LB_GETTEXTLEN, Index, 0); //通过使用消息取得数据长度,其实这个效率很低,不如先设一很长的字符串,然后确定长度
if Len = LB_ERR then Error(SListIndexError, Index);
SetLength(Result, Len);
if Len <> 0 then
begin
Len := SendMessage(ListBox.Handle, LB_GETTEXT, Index, Longint(PChar(Result))); //取得TEXT
SetLength(Result, Len); // LB_GETTEXTLEN isn't guaranteed to be accurate
end;
end;
end;
再举:
function TListBoxStrings.GetObject(Index: Integer): TObject;
begin
if ListBox.Style in [lbVirtual, lbVirtualOwnerDraw] then
Result := ListBox.DoGetDataObject(Index) //自定义取对象方法
else
begin
Result := TObject(ListBox.GetItemData(Index)); //取得窗口扩展的DATA内容
if Longint(Result) = LB_ERR then Error(SListIndexError, Index);
end;
end;
再看TLISTBOX的PAINT:
procedure TCustomListBox.WMPaint(var Message: TWMPaint);
procedure PaintListBox;
var
DrawItemMsg: TWMDrawItem; //充分利用了消息结构
MeasureItemMsg: TWMMeasureItem; //中间处理ITEM消息
DrawItemStruct: TDrawItemStruct; //消息画图需要数据内容
MeasureItemStruct: TMeasureItemStruct; //中间处理数据结构
R: TRect;
Y, I, H, W: Integer;
begin
if Items.Count = 0 then
begin
{ Just fill it in with the color }
with TBrush.Create do
try
Color := Self.Color;
FillRect(Message.DC, ClientRect, Handle);
finally
Free;
end;
Exit;
end;
{ Initialize drawing records }
DrawItemMsg.Msg := CN_DRAWITEM;
DrawItemMsg.DrawItemStruct := @DrawItemStruct;
DrawItemMsg.Ctl := Handle;
DrawItemStruct.CtlType := ODT_LISTBOX; //初始化结构
DrawItemStruct.itemAction := ODA_DRAWENTIRE;
DrawItemStruct.itemState := 0;
DrawItemStruct.hDC := Message.DC;
DrawItemStruct.CtlID := Handle;
DrawItemStruct.hwndItem := Handle;
{ Intialize measure records }
MeasureItemMsg.Msg := CN_MEASUREITEM; //初始中间处理结构
MeasureItemMsg.IDCtl := Handle;
MeasureItemMsg.MeasureItemStruct := @MeasureItemStruct;
MeasureItemStruct.CtlType := ODT_LISTBOX;
MeasureItemStruct.CtlID := Handle;
{ Draw the listbox }
Y := 0;
I := TopIndex;
GetClipBox(Message.DC, R);
H := Height;
W := Width;
while Y < H do
begin
MeasureItemStruct.itemID := I;
if I < Items.Count then
MeasureItemStruct.itemData := Longint(Pointer(Items.Objects[I]));
MeasureItemStruct.itemWidth := W;
MeasureItemStruct.itemHeight := FItemHeight;
DrawItemStruct.itemData := MeasureItemStruct.itemData;
DrawItemStruct.itemID := I;
Dispatch(MeasureItemMsg); //这里和下面的Dispatch(DrawItemMsg)对应很巧妙,触发CN_MEASUREITEM消息,这些消息是DELPHI自己处理的,不同COMBOBOX中的消息是WINDOWS定义的.
DrawItemStruct.rcItem := Rect(0, Y, MeasureItemStruct.itemWidth,
Y + Integer(MeasureItemStruct.itemHeight));
Dispatch(DrawItemMsg); //触发CN_DRAWITEM消息
Inc(Y, MeasureItemStruct.itemHeight);
Inc(I);
if I >= Items.Count then break;
end;
end;
begin
if Message.DC <> 0 then
{ Listboxes don't allow paint "sub-classing" like the other windows controls
so we have to do it ourselves. }
PaintListBox
else
inherited;
end;
----------Henry 20100711