You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让ListBox控件中仅指定项不可选中?

实现ListBox部分列表项不可选中

Windows原生ListBox的LBS_NOSEL属性会让所有项都无法选中,但要实现仅部分项不可选中,需要通过自绘ListBox+自定义状态标记的方式来实现,具体步骤如下:

1. 修改ListBox创建风格

首先要给ListBox添加自绘相关风格,让我们能控制项的绘制和交互逻辑:

hList = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", NULL,
    WS_VISIBLE | WS_BORDER | WS_CHILD | LBS_CENTER | WS_HSCROLL | WS_VSCROLL | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
    20, 55, 200, 120, hWindow, (HMENU)ID_LIST, hInstance, 0);

注意:原代码中的ES_CENTER是编辑框的风格,ListBox文本对齐应该用LBS_CENTER;新增的LBS_OWNERDRAWFIXED启用固定高度的自绘,LBS_HASSTRINGS保留ListBox的文本存储能力。

2. 给列表项标记可选中状态

添加项时,用LB_SETITEMDATA给每个项绑定一个布尔值,标记该项目是否可选中:

// 添加可选中项
int itemIdx = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)"可选中项1");
SendMessage(hList, LB_SETITEMDATA, itemIdx, (LPARAM)TRUE); // TRUE代表可选中

// 添加不可选中项
itemIdx = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)"不可选中项1");
SendMessage(hList, LB_SETITEMDATA, itemIdx, (LPARAM)FALSE); // FALSE代表不可选中

3. 自绘列表项(WM_DRAWITEM处理)

在窗口过程中处理WM_DRAWITEM消息,根据标记状态绘制不同样式的项(比如不可选中项用灰色文本):

case WM_DRAWITEM:
{
    LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
    if (lpDIS->CtlID == ID_LIST)
    {
        // 填充背景
        FillRect(lpDIS->hDC, &lpDIS->rcItem, (HBRUSH)(COLOR_WINDOW + 1));

        // 获取项文本
        TCHAR szText[256];
        SendMessage(lpDIS->hwndItem, LB_GETTEXT, lpDIS->itemID, (LPARAM)szText);

        // 获取项的可选中状态
        BOOL isSelectable = (BOOL)SendMessage(lpDIS->hwndItem, LB_GETITEMDATA, lpDIS->itemID, 0);

        // 设置文本颜色与背景色
        if (!isSelectable)
        {
            SetTextColor(lpDIS->hDC, GetSysColor(COLOR_GRAYTEXT));
        }
        else if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
        {
            SetTextColor(lpDIS->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
            SetBkColor(lpDIS->hDC, GetSysColor(COLOR_HIGHLIGHT));
        }
        else
        {
            SetTextColor(lpDIS->hDC, GetSysColor(COLOR_WINDOWTEXT));
            SetBkColor(lpDIS->hDC, GetSysColor(COLOR_WINDOW));
        }

        // 居中绘制文本
        DrawText(lpDIS->hDC, szText, -1, &lpDIS->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

        // 仅给可选中项绘制焦点框
        if ((lpDIS->itemState & ODS_FOCUS) && isSelectable)
        {
            DrawFocusRect(lpDIS->hDC, &lpDIS->rcItem);
        }

        return TRUE;
    }
    break;
}

4. 拦截选择操作,阻止不可选中项被选中

还要处理交互事件,避免用户通过鼠标或键盘选中不可选项目:

处理LBN_SELCHANGE通知

在WM_COMMAND中拦截列表项选中变更事件,取消不可选中项的选中状态:

case WM_COMMAND:
{
    int wmId = LOWORD(wParam);
    int wmEvent = HIWORD(wParam);
    switch (wmId)
    {
        case ID_LIST:
            if (wmEvent == LBN_SELCHANGE)
            {
                int curSel = SendMessage(hList, LB_GETCURSEL, 0, 0);
                if (curSel != LB_ERR)
                {
                    BOOL isSelectable = (BOOL)SendMessage(hList, LB_GETITEMDATA, curSel, 0);
                    if (!isSelectable)
                    {
                        SendMessage(hList, LB_SETCURSEL, -1, 0); // 取消选中
                    }
                }
            }
            break;
        // 其他命令处理逻辑...
    }
    break;
}

拦截鼠标点击

处理WM_LBUTTONDOWN,直接忽略对不可选中项的点击:

case WM_LBUTTONDOWN:
{
    POINT pt;
    GetCursorPos(&pt);
    ScreenToClient(hList, &pt);
    int itemIdx = SendMessage(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));
    if (itemIdx != LB_ERR)
    {
        BOOL isSelectable = (BOOL)SendMessage(hList, LB_GETITEMDATA, itemIdx, 0);
        if (!isSelectable)
        {
            return 0; // 阻止默认处理,不触发选中
        }
    }
    break;
}

处理键盘导航

处理WM_KEYDOWN,让上下箭头跳过不可选中项:

case WM_KEYDOWN:
{
    if (GetFocus() == hList)
    {
        int curSel = SendMessage(hList, LB_GETCURSEL, 0, 0);
        int itemCount = SendMessage(hList, LB_GETCOUNT, 0, 0);
        BOOL isSelectable;
        int newSel;

        switch (wParam)
        {
            case VK_UP:
                newSel = curSel - 1;
                while (newSel >= 0)
                {
                    isSelectable = (BOOL)SendMessage(hList, LB_GETITEMDATA, newSel, 0);
                    if (isSelectable)
                    {
                        SendMessage(hList, LB_SETCURSEL, newSel, 0);
                        break;
                    }
                    newSel--;
                }
                return 0;
            case VK_DOWN:
                newSel = curSel + 1;
                while (newSel < itemCount)
                {
                    isSelectable = (BOOL)SendMessage(hList, LB_GETITEMDATA, newSel, 0);
                    if (isSelectable)
                    {
                        SendMessage(hList, LB_SETCURSEL, newSel, 0);
                        break;
                    }
                    newSel++;
                }
                return 0;
        }
    }
    break;
}

内容的提问来源于stack exchange,提问作者nabi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:25:27