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

如何在不修改字体的情况下调整CListCtrl的CHeaderCtrl多行高度

问题:CListCtrl多行CHeaderCtrl无法调整高度(不修改字体)

我正在开发CListCtrl的多行(两行)CHeaderCtrl,但无法调整表头的高度。请问如何在不修改字体的情况下调整该高度?

以下是实现多行CHeaderCtrl的代码:

dlg.h 文件

#pragma once
#include "resource.h"
#include "framework.h"
#include <map>
#include <set>

using namespace std;

//My own header for multiline header
class CMyHeaderCtrl : public CHeaderCtrl
{
public:
    DECLARE_MESSAGE_MAP()

    void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult);
    void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg LRESULT OnLayout(HDLAYOUT* pLayout); // 添加布局处理方法
};

class CMFCtestDlg : public CDialogEx
{
    // Construction
public:
    CMFCtestDlg(CWnd *pParent = nullptr);   // standard constructor
    ~CMFCtestDlg() { }

    // Dialog Data
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MFC_TEST_DIALOG };
#endif
protected:
    map<int, LPCTSTR> m_mapOfTab{{0,L"Nula"},{1,L"Jedna"},{2,L"Dva"}};

public:
    CListCtrl m_cListCtrl;      // First list control
    CMyHeaderCtrl m_myHeaderCtrl;

protected:
    virtual void DoDataExchange(CDataExchange *pDX);    // DDX/DDV support

    // Implementation
protected:
    HICON m_hIcon;

    // Generated message map functions
    virtual BOOL OnInitDialog();
    DECLARE_MESSAGE_MAP()
    CMenu m_SplitMenu;
    CMenu m_SplitSubMenu;

public:
    void OnHeaderDropDown(NMHDR *pNMHDR, LRESULT *pResult);
};

dlg.cpp 文件

#include "pch.h"
#include "framework.h"
#include "MFC_test.h"
#include "MFC_testDlg.h"
#include "afxdialogex.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define _MAJU_PRG_NAME L"Majklův řez"

// CMFCtestDlg dialog
CMFCtestDlg::CMFCtestDlg(CWnd *pParent /*=nullptr*/)
    : CDialogEx(IDD_MFC_TEST_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_SplitMenu.LoadMenu(IDR_MENU);
}

void CMFCtestDlg::DoDataExchange(CDataExchange *pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST, m_cListCtrl);
}

BEGIN_MESSAGE_MAP(CMFCtestDlg, CDialogEx)
    ON_NOTIFY(HDN_DROPDOWN, IDC_LIST, &CMFCtestDlg::OnHeaderDropDown)
END_MESSAGE_MAP()

#define ID_MENU_ITEM1 35600
#define ID_MENU_ITEM2 35601

BOOL CMFCtestDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    m_cListCtrl.InsertColumn(0, L"Column 1", LVCFMT_CENTER, 100);
    m_cListCtrl.InsertColumn(1, L"FirstLine\nSecLin", LVCFMT_CENTER, 150);

    CHeaderCtrl *pHeaderCtrl = m_cListCtrl.GetHeaderCtrl();
    if(pHeaderCtrl)
    {
        m_myHeaderCtrl.SubclassWindow(pHeaderCtrl->GetSafeHwnd());

        pHeaderCtrl->ModifyStyle(0, pHeaderCtrl->GetStyle() | HDS_BUTTONS);
        // Modify the header item style to include the split button style
        HDITEM item;
        item.mask = HDI_FORMAT;
        pHeaderCtrl->GetItem(1, &item);
        item.fmt |= HDF_SPLITBUTTON;
        pHeaderCtrl->SetItem(1, &item);

        // 移除原SetWindowPos代码,改用HDM_LAYOUT处理高度
    }

    CString csTxt;
    map<int, LPCTSTR>::const_iterator it = m_mapOfTab.begin();
    for(int i = 0; it != m_mapOfTab.end(); ++it, ++i)
    {
        csTxt.Format(L"%i", it->first);
        m_cListCtrl.InsertItem(i, csTxt.GetString());
        m_cListCtrl.SetItemText(i, 1, it->second);
    }

    return TRUE;
}

void CMFCtestDlg::OnHeaderDropDown(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);

    // Get the header control
    CHeaderCtrl *pHeaderCtrl = m_cListCtrl.GetHeaderCtrl();

    if(pHeaderCtrl != nullptr && phdr->iItem >= 0)
    {
        // Calculate the drop-down button rectangle
        CRect rcItem;
        pHeaderCtrl->GetItemRect(phdr->iItem, &rcItem);

        // Get the position of the drop-down arrow button
        CPoint point(rcItem.left, rcItem.bottom); // Show menu below the header item
        pHeaderCtrl->ClientToScreen(&point); // Convert to screen coordinates

        // Track the popup menu (display it at the arrow location)
        m_SplitMenu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
    }
}

////////// CMyHeaderCtrl

BEGIN_MESSAGE_MAP(CMyHeaderCtrl, CHeaderCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyHeaderCtrl::OnCustomDraw)
    ON_WM_LBUTTONDOWN()
    ON_MESSAGE(HDM_LAYOUT, OnLayout) // 添加消息映射
END_MESSAGE_MAP()

LRESULT CMyHeaderCtrl::OnLayout(HDLAYOUT* pLayout)
{
    LRESULT result = CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, (LPARAM)pLayout);
    
    // 自定义表头高度,可根据需求调整数值
    const int customHeight = 60;
    pLayout->prc->bottom = pLayout->prc->top + customHeight;
    
    // 更新WINDOWPOS,通知CListCtrl适配新高度
    WINDOWPOS* pwpos = pLayout->pwpos;
    pwpos->cy = customHeight;
    
    return result;
}

void CMyHeaderCtrl::OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);

    if(pNMCD->dwDrawStage == CDDS_PREPAINT)
    {
        *pResult = CDRF_NOTIFYITEMDRAW;  // Request item draw notifications
    }
    else if(pNMCD->dwDrawStage == CDDS_ITEMPREPAINT)
    {
        *pResult = CDRF_SKIPDEFAULT;  // Skip default drawing

        // Set up the device context
        CDC *pDC = CDC::FromHandle(pNMCD->hdc);
        CRect rect = pNMCD->rc;
        int itemIndex = static_cast<int>(pNMCD->dwItemSpec);

        // Set background color
        pDC->FillSolidRect(&rect, RGB(240, 240, 240));

        // Draw two lines of text
        HDITEM hdItem;
        TCHAR text[256];
        hdItem.mask = HDI_TEXT;
        hdItem.pszText = text;
        hdItem.cchTextMax = sizeof(text) / sizeof(TCHAR);
        GetItem(itemIndex, &hdItem);

        CString headerText(hdItem.pszText);

        // Split text by newline character "\n"
        CString topText, bottomText;
        AfxExtractSubString(topText, headerText, 0, '\n');
        AfxExtractSubString(bottomText, headerText, 1, '\n');

        // Calculate positions for two lines of text
        CRect rectTop = rect;
        CRect rectBottom = rect;

        rectTop.bottom = rect.top + rect.Height() / 2;
        rectBottom.top = rectTop.bottom;

        // Draw the first and second line of text
        HDITEM item;
        item.mask = HDI_FORMAT;
        GetItem(pNMCD->dwItemSpec, &item);
        UINT uHdFormat = DT_LEFT;
        if(item.fmt & LVCFMT_RIGHT)
            uHdFormat = DT_RIGHT;
        else if(item.fmt & LVCFMT_CENTER)
            uHdFormat = DT_CENTER;

        pDC->DrawText(topText, &rectTop, uHdFormat | DT_VCENTER | DT_SINGLELINE);
        pDC->DrawText(bottomText, &rectBottom, uHdFormat | DT_VCENTER | DT_SINGLELINE);

        if(item.fmt & HDF_SPLITBUTTON)
        {
            CRect iconRect = rect;
            iconRect.left = iconRect.right - 16;
            DrawFrameControl(pDC->m_hDC, &iconRect, DFC_SCROLL, DFCS_SCROLLDOWN);
        }
    }
}

//Split Button
#define ID_OPTION1 32500
#define ID_OPTION2 32501

void CMyHeaderCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
    // Perform a hit-test to find the header item at the click position
    HDHITTESTINFO hitTestInfo = {0};
    hitTestInfo.pt = point;
    int nItem = SendMessage(HDM_HITTEST, 0, (LPARAM) &hitTestInfo);

    // Check if the click was on a split button
    if(nItem >= 0 && (hitTestInfo.flags & HHT_ONHEADER))
    {
        HDITEM hdItem;
        hdItem.mask = HDI_FORMAT;
        if(GetItem(nItem, &hdItem) && (hdItem.fmt & HDF_SPLITBUTTON))
        {
            CRect itemRect;
            if(!GetItemRect(nItem, &itemRect))
                return CHeaderCtrl::OnLButtonDown(nFlags, point);

            // Define an approximate width for the split button icon (e.g., 16 pixels)
            const int iconWidth = 16;

            // Calculate the area on the far right where the icon would be
            CRect iconRect = itemRect;
            iconRect.left = iconRect.right - iconWidth;

            // Check if the click point falls within the icon area
            if(iconRect.PtInRect(point))
            {
                // Show context menu or handle the split button click
                CMenu menu;
                menu.CreatePopupMenu();
                menu.AppendMenu(MF_STRING, ID_OPTION1, _T("Option 1"));
                menu.AppendMenu(MF_STRING, ID_OPTION2, _T("Option 2"));

                ClientToScreen(&point);
                menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);

                return; // Stop further processing
            }
        }
    }

    // Call base class for default behavior if it wasn't a split button click
    CHeaderCtrl::OnLButtonDown(nFlags, point);
}

关键修改说明

  1. 新增HDM_LAYOUT消息处理:拦截Header控件与父窗口的布局协商消息,强制设置自定义高度,避免系统默认的字体高度逻辑覆盖设置。
  2. 移除冲突代码:删除原OnInitDialog中的SetWindowPos调用,该代码会被CListCtrl后续布局操作覆盖,改用HDM_LAYOUT更可靠。
  3. 修正绘制区域:移除rect.bottom +=5的代码,确保绘制范围不超出Header控件实际客户区,避免内容显示异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:48:10