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;
}