如何在不修改字体的情况下调整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); }
关键修改说明
- 新增HDM_LAYOUT消息处理:拦截Header控件与父窗口的布局协商消息,强制设置自定义高度,避免系统默认的字体高度逻辑覆盖设置。
- 移除冲突代码:删除原
OnInitDialog中的SetWindowPos调用,该代码会被CListCtrl后续布局操作覆盖,改用HDM_LAYOUT更可靠。 - 修正绘制区域:移除
rect.bottom +=5的代码,确保绘制范围不超出Header控件实际客户区,避免内容显示异常。
内容的提问来源于stack exchange,提问作者Michal Hadraba
相关产品推荐
相关产品推荐

