I try to develope multiline (doubleline) header - CHeaderCtrl
of the CListCtrl
. I am not able to change the heigth of the header.How I can change the heigth without changing the font?
Code of doubleline CHeaderCtrl
:"dlg.h" file
#pragma once#include "resource.h"#include "framework.h"#include <map>#include <set>using namespace std;//My own header for multiline headerclass CMyHeaderCtrl : public CHeaderCtrl{public: DECLARE_MESSAGE_MAP() void OnCustomDraw(NMHDR *pNMHDR, LRESULT *pResult); void OnLButtonDown(UINT nFlags, CPoint point);};class CMFCtestDlg : public CDialogEx{ // Constructionpublic: CMFCtestDlg(CWnd *pParent = nullptr); // standard constructor ~CMFCtestDlg() { } // Dialog Data#ifdef AFX_DESIGN_TIME enum { IDD = IDD_MFC_TEST_DIALOG };#endifprotected: 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 // Implementationprotected: 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" file
#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 dialogCMFCtestDlg::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 35601BOOL 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; //item.iItem = 0; // Index of the header item you want to modify pHeaderCtrl->GetItem(1, &item); item.fmt |= HDF_SPLITBUTTON; pHeaderCtrl->SetItem(1, &item); RECT rect; pHeaderCtrl->GetClientRect(&rect); // rect.bottom += rect.bottom - rect.top; //Resize heigth of the Header to double heigth rect.bottom += 50; pHeaderCtrl->SetWindowPos(0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); //double headerHeight = rect.bottom - rect.top; } 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 // Create and display the context menu // Track the popup menu (display it at the arrow location) m_SplitMenu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); }}////////// CMyHeaderCtrlBEGIN_MESSAGE_MAP(CMyHeaderCtrl, CHeaderCtrl) ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyHeaderCtrl::OnCustomDraw) ON_WM_LBUTTONDOWN() ON_WM_PAINT()END_MESSAGE_MAP()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 rect.bottom += 5; 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 CHeaderCtrl *pHeaderCtrl = dynamic_cast<CHeaderCtrl *>(CWnd::FromHandle(pNMCD->hdr.hwndFrom)); HDITEM item; item.mask = HDI_FORMAT; pHeaderCtrl->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) { rect.left = rect.right - 16; DrawFrameControl(pDC->m_hDC, &rect, DFC_SCROLL, DFCS_SCROLLDOWN); } }}//Split Button#define ID_OPTION1 32500#define ID_OPTION2 32501void 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);}