TListBox

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

你可能感兴趣的:(TListBox)