带复选框的combox

1、设置为 fixed

2、代码

cpp 复制代码
#pragma once
#include "HeadAllSys.h"
/******************************
mahongkai 2025-11-12

类说明:带checkbox的combox

函数说明:
    1、参考 DataBaseViewDlg 中的使用
******************************/
// CCheckComboBox.h

// 自定义通知代码
#define CCBN_CHECKCHANGE (WM_USER + 100)

class CCheckComboBox : public CComboBox
{
    DECLARE_DYNAMIC(CCheckComboBox)

public:
    CCheckComboBox();
    virtual ~CCheckComboBox();

    // 添加带复选框的项
    int AddCheckString(CString lpszString, BOOL bChecked = FALSE);
    int InsertCheckString(int nIndex, LPCTSTR lpszString, BOOL bChecked = FALSE);

    // 复选框操作
    BOOL GetCheck(int nIndex);
    void SetCheck(int nIndex, BOOL bCheck);

    // 获取选中项的文本
    CString GetCheckedItemsText(LPCTSTR lpszDelimiter = _T(";"));

    // 获取选中项的索引
    void GetCheckedIndices(CUIntArray& indices);

    // 删除所有项
    void ResetContent();

protected:
    DECLARE_MESSAGE_MAP()

private:
    CArray<BOOL, BOOL> m_CheckStates;  // 存储每个项的选中状态
    CFont m_font;                      // 字体
    int m_nItemHeight;                 // 项高度
    BOOL m_bDropdown;                  // 下拉状态

    map<int, CString> m_ItemTexts;  // 自己存储所有字符串

    // 子类化下拉列表相关
    CWnd m_listBox;                  // 下拉列表窗口
    WNDPROC m_pOldListBoxProc;       // 原窗口过程

    // 初始化函数
    void Initialize();

    // 子类化下拉列表
    void SubclassListBox();

    // 静态窗口过程
    static LRESULT CALLBACK ListBoxProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

public:
    virtual void PreSubclassWindow();
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnCloseUp();
    afx_msg void OnDropDown();
};
cpp 复制代码
#include "pch.h"
#include "StylusProfilometerApp.h"
#include "CCheckComboBox.h"


IMPLEMENT_DYNAMIC(CCheckComboBox, CComboBox)

BEGIN_MESSAGE_MAP(CCheckComboBox, CComboBox)
    ON_WM_LBUTTONDOWN()
    ON_WM_CREATE()
    ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseUp)
    ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropDown)
END_MESSAGE_MAP()

CCheckComboBox::CCheckComboBox()
    : m_nItemHeight(20)
    , m_bDropdown(FALSE)
    , m_pOldListBoxProc(NULL)
{
}

CCheckComboBox::~CCheckComboBox()
{
    // 确保取消子类化
    if (m_listBox.m_hWnd && m_pOldListBoxProc)
    {
        m_listBox.UnsubclassWindow();
    }
}

// 初始化函数
void CCheckComboBox::Initialize()
{
    // 设置自绘属性
    ModifyStyle(0, CBS_OWNERDRAWFIXED | CBS_HASSTRINGS);

    // 创建字体
    if (!m_font.m_hObject)
    {
        CFont* pFont = GetFont();
        if (pFont)
        {
            LOGFONT lf;
            pFont->GetLogFont(&lf);
            m_font.CreateFontIndirect(&lf);
        }
        else
        {
            m_font.CreateStockObject(DEFAULT_GUI_FONT);
        }
    }

    // 设置初始文本
    SetWindowText(_T("请选择..."));
}

int CCheckComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CComboBox::OnCreate(lpCreateStruct) == -1)
        return -1;

    Initialize();
    return 0;
}

void CCheckComboBox::PreSubclassWindow()
{
    CComboBox::PreSubclassWindow();
    Initialize();
}

int CCheckComboBox::AddCheckString(CString lpszString, BOOL bChecked)
{
    // 2. 添加到 ComboBox - 添加空字符串或者索引标识
    int nIndex = AddString(lpszString);

    // 1. 先添加到自己的字符串数组
    m_ItemTexts.insert({ nIndex,lpszString });

    if (nIndex != CB_ERR && nIndex != CB_ERRSPACE)
    {
        if (nIndex >= m_CheckStates.GetSize())
            m_CheckStates.SetSize(nIndex + 1);
        m_CheckStates[nIndex] = bChecked;

        // 如果有项被选中,更新显示文本
        if (bChecked)
        {
            CString strText = GetCheckedItemsText();
            SetWindowText(strText);
        }
    }
    return nIndex;
}

int CCheckComboBox::InsertCheckString(int nIndex, LPCTSTR lpszString, BOOL bChecked)
{
    if (nIndex >= 0 && nIndex <= m_ItemTexts.size())
    {
        m_ItemTexts.insert({ nIndex, lpszString });
    }
    else
    {
        return CB_ERR;
    }

    int nResult = InsertString(nIndex, lpszString);
    if (nResult != CB_ERR && nResult != CB_ERRSPACE)
    {
        if (nResult >= m_CheckStates.GetSize())
            m_CheckStates.SetSize(nResult + 1);

        // 移动选中状态
        for (int i = m_CheckStates.GetSize() - 1; i > nResult; i--)
            m_CheckStates[i] = m_CheckStates[i - 1];

        m_CheckStates[nResult] = bChecked;

        // 如果有项被选中,更新显示文本
        if (bChecked)
        {
            CString strText = GetCheckedItemsText();
            SetWindowText(strText);
        }
    }
    return nResult;
}

BOOL CCheckComboBox::GetCheck(int nIndex)
{
    if (nIndex >= 0 && nIndex < m_CheckStates.GetSize())
        return m_CheckStates[nIndex];
    return FALSE;
}

void CCheckComboBox::SetCheck(int nIndex, BOOL bCheck)
{
    if (nIndex >= 0 && nIndex < m_CheckStates.GetSize())
    {
        m_CheckStates[nIndex] = bCheck;

        // 更新显示文本
        CString strText = GetCheckedItemsText();
        SetWindowText(strText);

        Invalidate();
    }
}

void CCheckComboBox::ResetContent()
{
    CComboBox::ResetContent();
    m_CheckStates.RemoveAll();
    m_ItemTexts.clear();
}

void CCheckComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (lpDrawItemStruct->itemID == -1)
        return;

    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    CRect rcItem = lpDrawItemStruct->rcItem;

    // 保存DC状态
    int nSavedDC = pDC->SaveDC();

    // 设置字体
    CFont* pOldFont = pDC->SelectObject(&m_font);

    // 绘制背景
    COLORREF crBackground, crText;
    if (lpDrawItemStruct->itemState & ODS_SELECTED)
    {
        crBackground = ::GetSysColor(COLOR_HIGHLIGHT);
        crText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
    }
    else
    {
        crBackground = ::GetSysColor(COLOR_WINDOW);
        crText = ::GetSysColor(COLOR_WINDOWTEXT);
    }

    pDC->FillSolidRect(rcItem, crBackground);
    pDC->SetTextColor(crText);
    pDC->SetBkColor(crBackground);

    // 绘制复选框
    CRect rcCheckBox = rcItem;
    rcCheckBox.right = rcCheckBox.left + 16;
    rcCheckBox.DeflateRect(2, (rcItem.Height() - 13) / 2, 2, (rcItem.Height() - 13) / 2);

    // 绘制复选框边框和背景
    pDC->DrawFrameControl(rcCheckBox, DFC_BUTTON, DFCS_BUTTONCHECK);

    // 如果选中,绘制勾号
    if (m_CheckStates[lpDrawItemStruct->itemID])
    {
        // 使用更粗的笔绘制勾号
        CPen pen(PS_SOLID, 2, RGB(0, 0, 0));
        CPen* pOldPen = pDC->SelectObject(&pen);

        // 绘制勾号
        pDC->MoveTo(rcCheckBox.left + 3, rcCheckBox.top + 6);
        pDC->LineTo(rcCheckBox.left + 6, rcCheckBox.top + 9);
        pDC->LineTo(rcCheckBox.left + 11, rcCheckBox.top + 4);

        pDC->SelectObject(pOldPen);
    }

    // 绘制文本 - 从自己的数组获取
    CString strText;
    strText = m_ItemTexts[lpDrawItemStruct->itemID];
    CRect rcText = rcItem;
    rcText.left += 20;  // 为复选框留出空间
    rcText.DeflateRect(2, 0);

    pDC->DrawText(strText, rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);

    // 恢复DC
    pDC->SelectObject(pOldFont);
    pDC->RestoreDC(nSavedDC);
}

void CCheckComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    lpMeasureItemStruct->itemHeight = m_nItemHeight;
}

void CCheckComboBox::OnLButtonDown(UINT nFlags, CPoint point)
{
    // 只调用基类处理,让下拉列表正常显示
    // 实际的复选框点击在下拉列表的窗口过程中处理
    CComboBox::OnLButtonDown(nFlags, point);
}

void CCheckComboBox::OnDropDown()
{
    m_bDropdown = TRUE;
    // 子类化下拉列表
    SubclassListBox();
}

void CCheckComboBox::OnCloseUp()
{
    m_bDropdown = FALSE;
    // 取消子类化
    if (m_listBox.m_hWnd && m_pOldListBoxProc)
    {
        m_listBox.UnsubclassWindow();
        m_listBox.Detach();
    }
}

void CCheckComboBox::SubclassListBox()
{
    COMBOBOXINFO cbi = { sizeof(COMBOBOXINFO) };
    if (GetComboBoxInfo(&cbi))
    {
        if (cbi.hwndList != NULL && m_listBox.m_hWnd != cbi.hwndList)
        {
            // 先取消之前的子类化
            if (m_listBox.m_hWnd && m_pOldListBoxProc)
            {
                m_listBox.UnsubclassWindow();
            }

            // 重新子类化
            if (m_listBox.SubclassWindow(cbi.hwndList))
            {
                m_pOldListBoxProc = (WNDPROC)SetWindowLongPtr(cbi.hwndList, GWLP_WNDPROC, (LONG_PTR)ListBoxProc);
                SetWindowLongPtr(cbi.hwndList, GWLP_USERDATA, (LONG_PTR)this);
            }
        }
    }
}

LRESULT CALLBACK CCheckComboBox::ListBoxProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    CCheckComboBox* pThis = (CCheckComboBox*)GetWindowLongPtr(hWnd, GWLP_USERDATA);

    if (pThis && message == WM_LBUTTONDOWN)
    {
        CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

        // 获取点击的项索引
        int nIndex = (int)::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(point.x, point.y));

        if (nIndex >= 0 && nIndex < pThis->GetCount())
        {
            // 切换选中状态
            pThis->m_CheckStates[nIndex] = !pThis->m_CheckStates[nIndex];

            // 更新显示文本
            CString strText = pThis->GetCheckedItemsText();
            pThis->SetWindowText(strText);

            // 重绘组合框和下拉列表
            pThis->Invalidate();
            ::InvalidateRect(hWnd, NULL, TRUE);

            // 发送自定义通知消息
            pThis->GetParent()->SendMessage(CCBN_CHECKCHANGE, (WPARAM)pThis->GetDlgCtrlID(), (LPARAM)pThis->m_hWnd);

            return 0; // 已处理,不传递给原窗口过程
        }
    }

    // 调用原窗口过程
    if (pThis && pThis->m_pOldListBoxProc)
    {
        return CallWindowProc(pThis->m_pOldListBoxProc, hWnd, message, wParam, lParam);
    }

    return ::DefWindowProc(hWnd, message, wParam, lParam);
}

CString CCheckComboBox::GetCheckedItemsText(LPCTSTR lpszDelimiter)
{
    CString strResult;
    for (int i = 0; i < m_ItemTexts.size(); i++)
    {
        if (i < m_CheckStates.GetSize() && m_CheckStates[i])
        {
            CString strItem = m_ItemTexts[i];

            if (!strResult.IsEmpty())
                strResult += lpszDelimiter;
            strResult += strItem;
        }
    }

    if (strResult.IsEmpty())
        strResult = _T("请选择...");

    return strResult;
}

void CCheckComboBox::GetCheckedIndices(CUIntArray& indices)
{
    indices.RemoveAll();
    for (int i = 0; i < GetCount(); i++)
    {
        if (m_CheckStates[i])
            indices.Add(i);
    }
}

3、使用

cpp 复制代码
    CCheckComboBox mCmbYAxis;

afx_msg LRESULT OnCheckChange(WPARAM wParam, LPARAM lParam);  // 处理复选框改变通知
  
ON_MESSAGE(CCBN_CHECKCHANGE, &DataBaseViewDlg::OnCheckChange)
// AddCheckString 使用示例
void cmbInit()
{
        mCmbYAxis.ResetContent();
        mCmbYAxis.AddCheckString(cstr, true);
.....

}


LRESULT dlg::OnCheckChange(WPARAM wParam, LPARAM lParam)
{   
    int nCmbId = (int)wParam;
    HWND hWndCtrl = (HWND)lParam;

    // 根据控件ID处理不同的组合框
    if (nCmbId == IDC_COMBOBOXEX_YAxis)  // 确保这个ID正确
    {
        // 获取选中的项文本
        CString strChecked = mCmbYAxis.GetCheckedItemsText();

    }

    return 0;
}
相关推荐
syker2 小时前
太极指令集架构(TCIS)v1.1与主流指令集比较研究报告
c++·架构
jf加菲猫3 小时前
第1章 认识Qt
开发语言·c++·qt·ui
蒋星熠5 小时前
全栈开发实战指南:从架构设计到部署运维
运维·c++·python·系统架构·node.js·devops·c5全栈
杜子不疼.5 小时前
【C++】深入拆解二叉搜索树:从递归与非递归双视角,彻底掌握STL容器的基石
开发语言·c++
天若有情6735 小时前
从零实现轻量级C++ Web框架:SimpleHttpServer入门指南
开发语言·前端·c++·后端·mvc·web应用
mjhcsp7 小时前
C++ 三分查找:在单调与凸函数中高效定位极值的算法
开发语言·c++·算法
Elnaij8 小时前
从C++开始的编程生活(13)——list和浅谈stack、queue
开发语言·c++
深思慎考12 小时前
微服务即时通讯系统(服务端)——用户子服务实现逻辑全解析(4)
linux·c++·微服务·云原生·架构·通讯系统·大学生项目
草莓火锅14 小时前
用c++使输入的数字各个位上数字反转得到一个新数
开发语言·c++·算法