Click here to Skip to main content
15,890,609 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi!
I have added image to row in second column of
CListCtrl
. But I can see a small space between image and grid in
C++
CListCtrl
. How can I remove it?
C++
iIndent
doesn't help!

Also you can see wider gride line when selection an item!
Tank you!
Posted
Comments
JJMatthews 13-Nov-13 7:24am    
Hello, I just had the exact same issue a couple months ago and your not going to like my solution. I tried everything and nothing worked, eventually I manually drew the whole thing. It wasnt really as much work as it sounded and it ended up being worth it in the end.
thomas_wingfield 14-Nov-13 4:08am    
Hello JJ) Can you tell me in details how did you do it? That's interesting just to know how somebody decided this proble:)
Thanks again for reply!
JJMatthews 14-Nov-13 5:45am    
I can post my list control class if you would like. hahaha I was hoping there was a way to do this that I didn't find but nobody has posted .. (I did spend at least a day on it)

thomas_wingfield 14-Nov-13 8:54am    
Please if you can) anyway I check it like accepted)
JJMatthews 14-Nov-13 9:10am    
There it goes, if you need any help using it let me know.

1 solution

No problem, here goes

The header file:

//===============================================================================
// ListCtrlHDOD.h : List Control with Header Drag-Drop, Owner-Draw Header, 
//					Column Show/Hide -w- Context Menu, Header Height Adjustment
//===============================================================================
#ifndef ___LISTCTRLHDOD_H___
#define ___LISTCTRLHDOD_H___
#pragma once

//===============================================================================
// CColumnData
//===============================================================================
class CColumnData : public CObject
{
public:
	CColumnData()
	{
		m_bHidden		= FALSE;
		m_bDisableHide	= FALSE;
		m_bResizeable	= TRUE;
		m_bSortable		= TRUE;
		m_iMinWidth		= 30;
		m_cstrToolTip	= _T("");
		m_iFormat		= LVCFMT_LEFT;
		m_iDefWidth		= 0;
		m_iCurWidth		= 0;
	}

	BOOL		m_bHidden;
	BOOL		m_bDisableHide;
	BOOL		m_bResizeable;
	BOOL		m_bSortable;
	int			m_iMinWidth;
	CString		m_cstrToolTip;

	int			m_iFormat;
	int			m_iDefWidth;
	int			m_iCurWidth;
};


//===============================================================================
// CHeaderCtrlHDOD
//===============================================================================
class CHeaderCtrlHDOD : public CHeaderCtrl
{
	friend class CListCtrlHDOD;
public:
	DECLARE_DYNAMIC(CHeaderCtrlHDOD)

	// Construction / Destruction
	CHeaderCtrlHDOD();
	virtual ~CHeaderCtrlHDOD();

protected:
	int				m_iHeight;
	CFont			m_Font;
	float			m_fFontHeight;
	COLORREF		m_crText;
	COLORREF		m_crLineLight;
	COLORREF		m_crLineDark;
	COLORREF		m_crBackStart;
	COLORREF		m_crBackEnd;

public:	
	// Implementation
	void SetHeight(int iHeight);

protected:
	// Overrides
	BOOL SubclassDlgItem(UINT nID, CWnd* pParent);

	// Messages
	DECLARE_MESSAGE_MAP()
	afx_msg void OnDestroy();
	afx_msg LRESULT OnLayout(WPARAM wParam, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);	
	afx_msg void OnHdnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnHdnItemDblClick(NMHDR* pNMHDR, LRESULT* pResult);
};


//===============================================================================
// CListCtrlHDOD
//===============================================================================
class CListCtrlHDOD : public CListCtrl
{
	friend class CHeaderCtrlHDOD;
public:
	DECLARE_DYNAMIC(CListCtrlHDOD)

	// Construction / Destruction
	CListCtrlHDOD();
	virtual ~CListCtrlHDOD();

protected:
	CHeaderCtrlHDOD		m_HeaderCtrl;

	// Properties
	int					m_iSortCol;
	bool				m_bSortAsc;
	int					m_iItemHeight;
	CString				m_cstrFontName;
	float				m_fItemFontHeight;
	float				m_fMenuFontHeight;
	COLORREF			m_crItemText;
	COLORREF			m_crDisItemText;
	COLORREF			m_crSelItemText;
	COLORREF			m_crItemBack;
	COLORREF			m_crSelItemBack;
	COLORREF			m_crMenuBack;
	COLORREF			m_crMenuBorder;
	COLORREF			m_crMenuBarStart;
	COLORREF			m_crMenuBarEnd;
	int					m_iMenuItemWidth;
	CMenu				m_HeaderMenu;
	PFNLVCOMPARE		m_pfnCompareFunc;

public:	
	// Property Accessor Functions
	int GetSortColumn(){return m_iSortCol;}
	void SetSortColumn(int i){m_iSortCol=i;}
	bool GetSortAscending(){return m_bSortAsc;}
	void SetSortAscending(bool b){m_bSortAsc=b;}
	int GetItemHeight(){return m_iItemHeight;}
	void SetItemHeight(int iHeight);
	COLORREF GetItemTextColor(){return m_crItemText;}
	void SetItemTextColor(COLORREF cr){__SET_VIS_PROP(m_crItemText, cr)};
	COLORREF GetDisabledItemTextColor(){return m_crDisItemText;}
	void SetDisabledItemTextColor(COLORREF cr){__SET_VIS_PROP(m_crDisItemText, cr)};
	COLORREF GetSelectedItemTextColor(){return m_crSelItemText;}
	void SetSelectedItemTextColor(COLORREF cr){__SET_VIS_PROP(m_crSelItemText, cr)};
	COLORREF GetItemBackColor(){return m_crItemBack;}
	void SetItemBackColor(COLORREF cr){__SET_VIS_PROP(m_crItemBack, cr)};
	COLORREF GetSelectedItemBackColor(){return m_crSelItemBack;}
	void SetSelectedItemBackColor(COLORREF cr){__SET_VIS_PROP(m_crSelItemBack, cr)};
	void SetCompareFunction(PFNLVCOMPARE pfn){m_pfnCompareFunc = pfn;}

	// Header Property Accessor Functions
	int GetHeaderHeight(){return m_HeaderCtrl.m_iHeight;}
	void SetHeaderHeight(int iHeight){m_HeaderCtrl.SetHeight(iHeight);}
	float GetHeaderFontHeight(){return m_HeaderCtrl.m_fFontHeight;}
	void SetHeaderFontHeight(float fHeight){m_HeaderCtrl.m_fFontHeight=fHeight;}

	// Implementation
	int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat, 
		int nWidth, int nSubItem, BOOL bHidden = FALSE, BOOL bDisableHide = FALSE,
		BOOL bResizeable = TRUE, BOOL bSortable = TRUE, int iMinWidth = 30, 
		CString cstrToolTip = _T(""));
	BOOL DeleteColumn(int nCol);
	void SortList();

protected:
	// Sort Callback
	static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

	// Overrides
	virtual void PreSubclassWindow();
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

	// Messages
	DECLARE_MESSAGE_MAP()
	afx_msg void OnDestroy();
	afx_msg void OnPaint();
	afx_msg BOOL OnLvnColumnClick(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg BOOL OnLvnItemChanged(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnHdnBegintrack(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnHdnTrack(NMHDR* pNMHDR, LRESULT *pResult);
	afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
	afx_msg void OnDrawItem(int nIDCtl, DRAWITEMSTRUCT* pDI);
	afx_msg void OnMeasureItem(int nIDCtl, MEASUREITEMSTRUCT* pMI);
	afx_msg void MeasureItem(MEASUREITEMSTRUCT* pMI);
};


#endif // ___LISTCTRLHDOD_H___


The cpp file:
//===============================================================================
// ListCtrlHDOD.cpp : 
//===============================================================================
#include "stdafx.h"
#include "ListCtrlHDOD.h"
#include "DbConfig.h"

//===============================================================================
// CHeaderCtrlHDOD 
//===============================================================================
BEGIN_MESSAGE_MAP(CHeaderCtrlHDOD, CHeaderCtrl)
	ON_WM_DESTROY()
	ON_MESSAGE(HDM_LAYOUT, &CHeaderCtrlHDOD::OnLayout)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SETCURSOR()
	ON_NOTIFY(HDN_BEGINDRAG, 0, &CHeaderCtrlHDOD::OnHdnBeginDrag)
	ON_NOTIFY(HDN_ITEMDBLCLICKA, 0, &CHeaderCtrlHDOD::OnHdnItemDblClick)
	ON_NOTIFY(HDN_ITEMDBLCLICKW, 0, &CHeaderCtrlHDOD::OnHdnItemDblClick)
END_MESSAGE_MAP()

IMPLEMENT_DYNAMIC(CHeaderCtrlHDOD, CHeaderCtrl)

//=================================================
// Construction / Destruction
//=================================================
CHeaderCtrlHDOD::CHeaderCtrlHDOD()
{
	m_iHeight			= ((CApp*)AfxGetApp())->m_iListHeaderHeight;
	m_fFontHeight		= ((CApp*)AfxGetApp())->m_fListHeaderFontHeight;
	m_crText			= ((CApp*)AfxGetApp())->m_crListHeaderText;
	m_crLineLight		= RGB(255,255,255);//((CApp*)AfxGetApp())->m_crListHeaderDivLight;
	m_crLineDark		= RGB(157,157,161);//((CApp*)AfxGetApp())->m_crListHeaderDivDark;
	m_crBackStart		= ((CApp*)AfxGetApp())->m_crListHeaderStart;
	m_crBackEnd			= ((CApp*)AfxGetApp())->m_crListHeaderEnd;
}

CHeaderCtrlHDOD::~CHeaderCtrlHDOD()
{
}

//=================================================
// Overrides
//=================================================
BOOL CHeaderCtrlHDOD::SubclassDlgItem(UINT nID, CWnd* pParent)
{
	if(!CHeaderCtrl::SubclassDlgItem(nID, pParent))
		return FALSE;
	SetHeight(m_iHeight);
	return TRUE;
}

//=================================================
// Messages
//=================================================
void CHeaderCtrlHDOD::OnDestroy()
{
	CHeaderCtrl::OnDestroy();

	// Delete header item data
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	while(GetItemCount() > 0)
	{
		VERIFY(GetItem(0, &hdi));
		CColumnData* pColData = (CColumnData*)hdi.lParam;
		ASSERT(pColData);
		delete pColData;
		DeleteItem(0);
	}
}

LRESULT CHeaderCtrlHDOD::OnLayout(WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult = CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam);

	HD_LAYOUT& hdl = *(HD_LAYOUT*)lParam;
	RECT* prc = hdl.prc;
	WINDOWPOS* pwpos = hdl.pwpos;

	pwpos->cy = m_iHeight;
	prc->top = m_iHeight;

	return lResult;
}

void CHeaderCtrlHDOD::OnPaint()
{
	CPaintDC dc(this); 
	CRect rClient;
	GetClientRect(&rClient);

	CListCtrlHDOD* pParent = (CListCtrlHDOD*)GetParent();
	bool bNoSort = ((pParent->GetStyle() & LVS_NOSORTHEADER) == LVS_NOSORTHEADER);

	// Double buffer to eliminate flashing
	CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
	CBitmap bmp;
	bmp.CreateDiscardableBitmap(&dc, 
		rClient.Width(), rClient.Height());
	dcMem.SelectObject(&bmp);

	dcMem.FillSolidRect(rClient, RGB(224,226,235));

	Graphics gfx(dcMem.GetSafeHdc());
	gfx.SetTextRenderingHint(TextRenderingHintClearTypeGridFit);
	gfx.SetSmoothingMode(SmoothingModeAntiAlias);

	Color clrBackStart, clrBackEnd;
	clrBackStart.SetFromCOLORREF(m_crBackStart);
	clrBackEnd.SetFromCOLORREF(m_crBackEnd);
	Color clrLineLight, clrLineDark;
	clrLineLight.SetFromCOLORREF(m_crLineLight);
	clrLineDark.SetFromCOLORREF(m_crLineDark);
	Pen pnLineLight(clrLineLight, 1);
	Pen pnLineDark(clrLineDark, 1);	

	for(int x = 0; x < GetItemCount(); x++)
	{
		// Get item area
		CRect rItem;
		GetItemRect(x, &rItem);

		// Get column info
		LVCOLUMN lvc = {0};
		lvc.mask = LVCF_FMT;
		pParent->GetColumn(x, &lvc);

		// Get item text
		HDITEM hdi = {0};
		TCHAR  szBuffer[256] = {_T('\0')};
		hdi.pszText = szBuffer;
		hdi.cchTextMax = 255;
		hdi.mask = HDI_TEXT;//|HDI_LPARAM;
		VERIFY(GetItem(x, &hdi));
		CString strItemText(hdi.pszText);
		//CColumnData* pColData = (CColumnData*)hdi.lParam;
		//ASSERT(pColData);
		
		LinearGradientBrush brBack(RC2GPR(rItem), 
			clrBackStart, clrBackEnd, 
			LinearGradientModeVertical);
		gfx.FillRectangle(&brBack, RC2GPR(rItem));

		// Paint item background
		if(!bNoSort)
		{
			// Paint item border
			rItem.InflateRect(0, -2, -1, -2);
			if(rItem.left > 0) {
				gfx.DrawLine(&pnLineDark,  
					Point(rItem.left, rItem.top), 
					Point(rItem.left, rItem.bottom));
			}
			gfx.DrawLine(&pnLineLight, 
				Point(rItem.right, rItem.top), 
				Point(rItem.right, rItem.bottom));

			// Draw sort marker
			rItem.InflateRect(0, 0, -5, 0);
			if(x == pParent->m_iSortCol)
			{
				// 10 X 6
				CRect rMarker(
					rItem.right - 10, rItem.CenterPoint().y - 3, 
					rItem.right, rItem.CenterPoint().y + 3 );
				if(pParent->m_bSortAsc)
				{
					gfx.DrawLine(&pnLineDark,  
						Point(rMarker.left, rMarker.bottom), 
						Point(rMarker.right, rMarker.bottom));
					gfx.DrawLine(&pnLineDark,  
						Point(rMarker.left, rMarker.bottom - 1), 
						Point(rMarker.left + 5, rMarker.top));
					gfx.DrawLine(&pnLineLight,  
						Point(rMarker.right, rMarker.bottom - 1), 
						Point(rMarker.right - 5, rMarker.top));
				}
				else
				{
					gfx.DrawLine(&pnLineDark,  
						Point(rMarker.left, rMarker.top), 
						Point(rMarker.right, rMarker.top));
					gfx.DrawLine(&pnLineDark,  
						Point(rMarker.left, rMarker.top + 1), 
						Point(rMarker.left + 5, rMarker.bottom));
					gfx.DrawLine(&pnLineLight,  
						Point(rMarker.right, rMarker.top + 1), 
						Point(rMarker.right - 5, rMarker.bottom));
				}
				rItem.right -= 11;
			}
		}

		// Draw item text
		rItem.InflateRect(-2, -2, 0, -2);
		if(strItemText.GetLength() > 0)
		{
			Color clrText;
			clrText.SetFromCOLORREF(m_crText);
			SolidBrush brItemText(clrText);
			Font fntItem(CT2OLE(pParent->m_cstrFontName), 
				m_fFontHeight, FontStyleRegular);
			StringFormat strfmt;
			strfmt.SetAlignment(StringAlignmentNear);
			if(lvc.fmt & LVCFMT_CENTER)
				strfmt.SetAlignment(StringAlignmentCenter);
			if(lvc.fmt & LVCFMT_RIGHT)
				strfmt.SetAlignment(StringAlignmentFar);
			strfmt.SetLineAlignment(StringAlignmentCenter);
			strfmt.SetTrimming(StringTrimmingEllipsisCharacter);
			gfx.DrawString(CT2OLE(strItemText), 
				strItemText.GetLength(), &fntItem, 
				RC2GPRF(rItem), &strfmt, &brItemText);
		}
	}

	// Update the screen
	dc.BitBlt(0, 0, rClient.Width(), rClient.Height(), &dcMem, 0, 0, SRCCOPY);
}

BOOL CHeaderCtrlHDOD::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;
}

BOOL CHeaderCtrlHDOD::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg)
{
	// Hit test the header control
	CPoint pt;
	GetCursorPos(&pt);
	ScreenToClient(&pt);
	HDHITTESTINFO hhti = {0};
	hhti.pt.x = pt.x;
	hhti.pt.y = pt.y;
	SendMessage(HDM_HITTEST, 0, (LPARAM)&hhti);
	if(hhti.iItem != -1)
	{
		HDITEM hdi = {0};
		hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_LPARAM;
		VERIFY(GetItem(hhti.iItem, &hdi));
		CColumnData* pColData = (CColumnData*)hdi.lParam;
		ASSERT(pColData);
		if((hdi.cxy <= 0) || (!pColData->m_bResizeable))
			return 1;
	}
	return CHeaderCtrl::OnSetCursor(pWnd, nHitTest, msg);
}

void CHeaderCtrlHDOD::OnHdnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMHEADER phdr = reinterpret_cast<lpnmheader>(pNMHDR);
	*pResult = 0;
	// TODO : Add 'CanDrag' property to ColumnData and implement here
}

void CHeaderCtrlHDOD::OnHdnItemDblClick(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMHEADER phdr = reinterpret_cast<lpnmheader>(pNMHDR);
	*pResult = 0;
	// TODO : When user double-clicks on a column resize column to widest string
}

//=================================================
// Implementation
//=================================================
void CHeaderCtrlHDOD::SetHeight(int iHeight)
{
	m_iHeight = iHeight;
	return;

	// The old way to set the height
	//if(!GetSafeHwnd())
	//	return;
	//if(m_Font.GetSafeHandle())
	//	m_Font.DeleteObject();

	//CClientDC dc(this);
	//CString cstrFontName = ((CMsgListCtrl*)GetParent())->m_cstrFontName;
	//int iFntHeight = CalcFontHeight(&dc, _T("XX"), CRect(0,0,500,iHeight), cstrFontName); 

	//VERIFY(m_Font.CreateFont(iFntHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, 
	//	ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
	//	DEFAULT_PITCH|FF_SWISS, cstrFontName));
}

//===============================================================================
// CListCtrlHDOD 
//===============================================================================
BEGIN_MESSAGE_MAP(CListCtrlHDOD, CListCtrl)
	ON_WM_DESTROY()
	ON_WM_PAINT()
	ON_WM_CONTEXTMENU()
	ON_WM_DRAWITEM()
	ON_WM_MEASUREITEM()
	ON_WM_MEASUREITEM_REFLECT()
	ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnLvnColumnClick)
	ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnLvnItemChanged)
	ON_NOTIFY(HDN_BEGINTRACKA, 0, OnHdnBegintrack)
	ON_NOTIFY(HDN_BEGINTRACKW, 0, OnHdnBegintrack)
	ON_NOTIFY(HDN_TRACKA, 0, OnHdnTrack)
	ON_NOTIFY(HDN_TRACKW, 0, OnHdnTrack)
END_MESSAGE_MAP()

IMPLEMENT_DYNAMIC(CListCtrlHDOD, CListCtrl)

//=================================================
// Construction / Destruction
//=================================================
CListCtrlHDOD::CListCtrlHDOD()
{
	m_iSortCol				= 0;
	m_bSortAsc				= true;
	m_cstrFontName			= ((CApp*)AfxGetApp())->m_cstrFontName;
	m_fItemFontHeight		= 8;
	m_fMenuFontHeight		= 8;	
	m_crItemText			= RGB(0,0,0);
	m_crDisItemText			= RGB(128,128,128);
	m_crSelItemText			= RGB(255,255,255);
	m_crItemBack			= RGB(255,255,255);
	m_crSelItemBack			= RGB(10,36,106);//RGB(0,152,206);
	m_crMenuBack			= RGB(255,255,255);
	m_crMenuBorder			= RGB(128,128,128);
	m_crMenuBarStart		= ((CApp*)AfxGetApp())->m_crGradStart; 
	m_crMenuBarEnd			= ((CApp*)AfxGetApp())->m_crGradEnd;
	m_iMenuItemWidth		= 0;
	// Set default compare function
	m_pfnCompareFunc		= CListCtrlHDOD::CompareFunc;
}

CListCtrlHDOD::~CListCtrlHDOD()
{
}

//=================================================
// Overrides
//=================================================
void CListCtrlHDOD::PreSubclassWindow()
{
	CListCtrl::PreSubclassWindow();

	DWORD dwStyle = GetStyle();

	// Check for bad styles
	if(dwStyle & LVS_AUTOARRANGE)
	{
		TRACE(_T("The CListCtrlHDOD class cannot have the LVS_AUTOARRANGE style\n"));
		ASSERT(0);
	}
	if(dwStyle & LVS_OWNERDATA)
	{
		TRACE(_T("The CListCtrlHDOD class cannot have the LVS_OWNERDATA style\n"));
		ASSERT(0);
	}
	
	// Check for required styles
	if(!(dwStyle & LVS_REPORT))
	{
		TRACE(_T("The CListCtrlHDOD class must have the LVS_REPORT style\n"));
		ASSERT(0);
	}
}

BOOL CListCtrlHDOD::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if(HIWORD(wParam) == 0)
	{
		int iCol = LOWORD(wParam);
		// Get header item (lParam)
		HDITEM hdi = {0};
		hdi.mask = HDI_LPARAM;
		VERIFY(m_HeaderCtrl.GetItem(iCol, &hdi));
		CColumnData* pColData = (CColumnData*)hdi.lParam;
		ASSERT(pColData);
		if(GetColumnWidth(iCol) == 0)
			SetColumnWidth(iCol, pColData->m_iCurWidth);
		else
			SetColumnWidth(iCol, 0);
	}

	return CListCtrl::OnCommand(wParam, lParam);
}

//=================================================
// Messages
//=================================================
void CListCtrlHDOD::OnDestroy()
{
	CListCtrl::OnDestroy();

}

void CListCtrlHDOD::OnPaint()
{
	if(!(GetStyle() & LVS_OWNERDRAWFIXED))
	{
		CListCtrl::Default();
		return;
	}

	CPaintDC dc(this); 
	CRect rClient;
	GetClientRect(&rClient);
	
	CRect rView = rClient;
	int iHOffset = GetScrollPos(SB_HORZ);
	int iVOffset = GetScrollPos(SB_VERT);
	rView.OffsetRect(iHOffset, iVOffset);

	// Double buffer 
	CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
	CBitmap bmpMem;
	bmpMem.CreateDiscardableBitmap(&dc, 
		rClient.Width(), rClient.Height());
	dcMem.SelectObject(&bmpMem);

	// Fillbackground
	dcMem.FillSolidRect(rClient, m_crItemBack);

	Graphics gfx(dcMem.GetSafeHdc());
	Color clrBack, clrText;
	Font fnt(CT2OLE(m_cstrFontName), m_fItemFontHeight, FontStyleRegular);

	// Turn on high quality image resizing algorithm
	gfx.SetInterpolationMode(InterpolationModeHighQualityBicubic);
	// Turn on anti-alias text rendering
	gfx.SetTextRenderingHint(TextRenderingHintAntiAlias);

	// Get pointer to our header control
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	ASSERT(pHeaderCtrl);

	// Draw all items
	for(int iItem = 0; iItem < GetItemCount(); iItem ++)
	{
		// Get item rect
		CRect rItem;
		GetItemRect(iItem, &rItem, LVIR_BOUNDS);

		// Skip invisible items
		if((rItem.bottom < rView.top) || (rItem.top > rView.bottom))
			continue;

		// Get item state
		LV_ITEM lvi = {0};
		lvi.mask = LVIF_STATE;
		lvi.iItem = iItem;
		lvi.stateMask = 0xFFFF; 
		GetItem(&lvi);
		bool bSelected = ((lvi.state & LVIS_SELECTED) == LVIS_SELECTED);

		clrBack.SetFromCOLORREF(bSelected ? m_crSelItemBack : m_crItemBack);
		clrText.SetFromCOLORREF(bSelected ? m_crSelItemText : m_crItemText);
		SolidBrush brBack(clrBack);
		SolidBrush brText(clrText);

		for(int iCol = 0; iCol < pHeaderCtrl->GetItemCount(); iCol ++)
		{
			// Get column format
			LVCOLUMN lvc = {0};
			lvc.mask = LVCF_FMT;
			GetColumn(iCol, &lvc);
			// Get sub-item coordinates
			CRect rSubItem;
			GetSubItemRect(iItem, iCol, LVIR_LABEL, rSubItem);
			// Paint sub-item background
			gfx.FillRectangle(&brBack, RC2GPRF(rSubItem));
			// Check for image column
			if(lvc.fmt & LVCFMT_IMAGE)//LVCFMT_COL_HAS_IMAGES)
			{
				// Get image index
				LV_ITEM lvisi = {0};
				lvisi.mask = LVIF_IMAGE;
				lvisi.iItem = iItem;
				lvisi.iSubItem = iCol;
				GetItem(&lvisi);
				if(lvisi.iImage >= 0)
				{
					// Get imagelist
					CImageList* pIL = GetImageList(LVSIL_SMALL);
					if(pIL)
					{
						// Get image size
						int cx = 0;
						int cy = 0;
						if(ImageList_GetIconSize(pIL->GetSafeHandle(), &cx, &cy))
						{
							// Draw the image centered
							CPoint pt(
								rSubItem.left + ((rSubItem.Width() - cx) / 2), 
								rSubItem.top + ((rSubItem.Height() - cy) / 2));
							CSize sz(cx, cy);
							pIL->DrawEx(&dcMem, lvisi.iImage, pt, sz, CLR_NONE, CLR_NONE, ILD_TRANSPARENT);
							// Adjust rect, for some reason column 0 will always be 
							//  shifted if there is any columns with images
							if(iCol > 0)
								rSubItem.left += (cx + 2);
						}
					}
				}
			}

			// Draw text 
			CString cstrText = GetItemText(iItem, iCol);
			if(cstrText.IsEmpty())
				continue;
			// Setup text alignment using column format
			StringFormat strfmt;
			strfmt.SetAlignment(StringAlignmentNear);
			strfmt.SetLineAlignment(StringAlignmentCenter);
			if(lvc.fmt & LVCFMT_CENTER)
				strfmt.SetAlignment(StringAlignmentCenter);
			if(lvc.fmt & LVCFMT_RIGHT)
				strfmt.SetAlignment(StringAlignmentFar);
			strfmt.SetFormatFlags(StringFormatFlagsNoWrap);
			strfmt.SetTrimming(StringTrimmingEllipsisCharacter);
			// Draw the text
			gfx.DrawString(CT2OLE(cstrText), 
				cstrText.GetLength(), &fnt, 
				RC2GPRF(rSubItem), &strfmt, &brText);
		 }
	}
	// Update the screen
	dc.BitBlt(0, 0, rClient.Width(), rClient.Height(), &dcMem, 0, 0, SRCCOPY);
}

BOOL CListCtrlHDOD::OnLvnColumnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<lpnmlistview>(pNMHDR);
	*pResult = 0;

	// The parent setting the ComapreFunc to NULL indicates 
	//   that all sorting will be handled by parent
	if(m_pfnCompareFunc == NULL)
		return FALSE; // Allow parent to receive this message too

	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	ASSERT(pHeaderCtrl);
	CRect rSortItem;
	pHeaderCtrl->GetItemRect(m_iSortCol, &rSortItem);

	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	VERIFY(pHeaderCtrl->GetItem(pNMLV->iSubItem, &hdi));
	CColumnData* pColData = (CColumnData*)hdi.lParam;
	ASSERT(pColData);
	if(!pColData->m_bSortable)
		return FALSE;

	if(m_iSortCol == pNMLV->iSubItem)
	{
		m_bSortAsc = !m_bSortAsc;
		InvalidateRect(rSortItem);
	}
	else
	{
		m_iSortCol = pNMLV->iSubItem;
		CRect rNewItem;
		pHeaderCtrl->GetItemRect(m_iSortCol, &rNewItem);
		InvalidateRect(rSortItem);
		InvalidateRect(rNewItem);
	}

	// Sort the list control
	SortList();

	return FALSE; // Allow parent to receive this message too
}

BOOL CListCtrlHDOD::OnLvnItemChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
	NM_LISTVIEW* pNMLV = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;

	if(GetStyle() & LVS_OWNERDRAWFIXED)
	{
		// Redraw selected item
		CRect rItem;
		GetItemRect(pNMLV->iItem, &rItem, LVIR_BOUNDS);
		InvalidateRect(rItem);
	}

	return FALSE; // Allow parent to receive this message too
}

void CListCtrlHDOD::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMHEADER phdr = reinterpret_cast<lpnmheader>(pNMHDR);
	*pResult = 0;

	// Get header item (lParam)
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	VERIFY(m_HeaderCtrl.GetItem(phdr->iItem, &hdi));
	CColumnData* pColData = (CColumnData*)hdi.lParam;
	ASSERT(pColData);
	if((!pColData->m_bResizeable) || (GetColumnWidth(phdr->iItem) == 0))
		*pResult = 1;
}

void CListCtrlHDOD::OnHdnTrack(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMHEADER phdr = reinterpret_cast<lpnmheader>(pNMHDR);
	*pResult = 0;

	// Get header item (lParam)
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	VERIFY(m_HeaderCtrl.GetItem(phdr->iItem, &hdi));
	CColumnData* pColData = (CColumnData*)hdi.lParam;
	ASSERT(pColData);
	if(phdr->pitem->cxy < pColData->m_iMinWidth)
		phdr->pitem->cxy = pColData->m_iMinWidth;
	pColData->m_iCurWidth = phdr->pitem->cxy;
}

void CListCtrlHDOD::OnContextMenu(CWnd* pWnd, CPoint point)
{
	// See if right-click was in listview header
	CPoint pt = point;
	ScreenToClient(&pt);
	CRect rHeader;
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	ASSERT(pHeaderCtrl);
	pHeaderCtrl->GetClientRect(&rHeader);
	if(!rHeader.PtInRect(pt))
		return;

	// Create context menu
	if(!m_HeaderMenu.CreatePopupMenu())
		return;

	// Measure strings as we build the menu
	m_iMenuItemWidth = 0;
	Font fnt(CT2OLE(m_cstrFontName), m_fMenuFontHeight, FontStyleRegular);
	Graphics gfx(::GetDC(NULL));

	// Build menu with all columns and their states
	for(int x = 0; x < pHeaderCtrl->GetItemCount(); x++)
	{
		HDITEM hdi = {0};
		TCHAR  szText[256] = {_T('\0')};
		hdi.pszText = szText;
		hdi.cchTextMax = 255;
		hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_LPARAM;
		VERIFY(pHeaderCtrl->GetItem(x, &hdi));

		// TODO : make this work
		StringReplace(hdi.pszText, -1, _T('\n'), _T(' '));

		UINT nFlags = MF_BYPOSITION|MF_STRING|MF_OWNERDRAW;
		CColumnData* pColData = (CColumnData*)hdi.lParam;
		if(!pColData->m_bDisableHide)
			nFlags |= ((hdi.cxy > 0) ? MF_CHECKED : MF_UNCHECKED);
		else
			nFlags |= MF_GRAYED;
		m_HeaderMenu.InsertMenu(x, nFlags, x, szText);

		RectF rect;
		CString str(hdi.pszText);
		gfx.MeasureString(CT2OLE(str), str.GetLength(), &fnt, RectF(0, 0, 500, 500), &rect);
		if(rect.Width > m_iMenuItemWidth)
			m_iMenuItemWidth = (int)rect.Width;
	}

	// Show Menu
	m_HeaderMenu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this, 0);
	m_HeaderMenu.DestroyMenu();
}

void CListCtrlHDOD::OnDrawItem(int nIDCtl, DRAWITEMSTRUCT* pDI)
{
	if(pDI->CtlType != ODT_MENU)
	{
		CListCtrl::OnDrawItem(nIDCtl, pDI);
		return;
	}

	CDC* pDC = CDC::FromHandle(pDI->hDC);
	ASSERT(pDC);
	Graphics gfx(pDC->GetSafeHdc());
	gfx.SetSmoothingMode(SmoothingModeAntiAlias);
	CRect rItem(pDI->rcItem);
	UINT nState = pDI->itemState;
	BOOL bDisabled	= ((nState & ODS_DISABLED)||(nState & ODS_GRAYED));
	BOOL bChecked = (nState & ODS_CHECKED);
	BOOL bHighlight = ((pDI->itemState & ODS_SELECTED) && (pDI->itemAction & (ODA_SELECT|ODA_DRAWENTIRE)));

	/*TODO : handle itemAction ?  app flashes when change hilight too fast
	Specifies the drawing action required. This member can be one or more of the values. 
	ODA_DRAWENTIRE - The entire control needs to be drawn.
	ODA_FOCUS - The control has lost or gained the keyboard focus. The itemState member should be checked to determine whether the control has the focus.
	ODA_SELECT - The selection status has changed. The itemState member should be checked to determine the new selection state.*/
		
	if(!(pDI->itemAction & ODA_DRAWENTIRE))
	{
		int z = 0;
	}

	// Get the menu item text
	CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
	ASSERT(pHeaderCtrl);
	HDITEM hdi = {0};
	TCHAR  szText[256] = {_T('\0')};
	hdi.pszText = szText;
	hdi.cchTextMax = 255;
	hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_LPARAM;
	VERIFY(pHeaderCtrl->GetItem(pDI->itemID, &hdi));
	CString strText = szText;

	CRect rMenuBar = rItem;
	rMenuBar.right = rItem.left + 24;
	Color clrMenuBarStart;
	clrMenuBarStart.SetFromCOLORREF(m_crMenuBarStart);
	Color clrMenuBarEnd;
	clrMenuBarEnd.SetFromCOLORREF(m_crMenuBarEnd);
	LinearGradientBrush brMenuBar(RC2GPR(rMenuBar), 
		clrMenuBarStart, clrMenuBarEnd, 
			LinearGradientModeHorizontal);
	gfx.FillRectangle(&brMenuBar, RC2GPR(rMenuBar));

	if(bChecked)
	{
		CRect rCheckBox = rMenuBar;
		//rCheckBox.InflateRect(-1,-2,-3,-2);
		rCheckBox.InflateRect(-2,-3,-4,-3);
		SolidBrush br(Color(20,0,0,128));
		gfx.FillRectangle(&br, RC2GPR(rCheckBox));
		Pen pnCheckFrame(Color(255,0,0,128), 1);
		Pen pnCheckMark(Color(255,0,0,0), 1.5);
		gfx.DrawRectangle(&pnCheckFrame, RC2GPR(rCheckBox));
		CPoint ptCenter = rCheckBox.CenterPoint();
		//gfx.DrawLine(&pnCheckMark, ptCenter.x, ptCenter.y + 3, ptCenter.x - 3, ptCenter.y );
		//gfx.DrawLine(&pnCheckMark, ptCenter.x, ptCenter.y + 3, ptCenter.x + 5, ptCenter.y - 3);
		gfx.DrawLine(&pnCheckMark, ptCenter.x, ptCenter.y + 3, ptCenter.x - 2, ptCenter.y );
		gfx.DrawLine(&pnCheckMark, ptCenter.x, ptCenter.y + 3, ptCenter.x + 5, ptCenter.y - 2);
	}

	CRect rText = rItem;
	rText.left += 24;
	Color clrBack;
	clrBack.SetFromCOLORREF(m_crMenuBack);
	SolidBrush brBack(clrBack);
	gfx.FillRectangle(&brBack, RC2GPR(rText));

	if(strText.GetLength() > 0)
	{
		rText.left += 2;
		Font fnt(CT2OLE(m_cstrFontName), m_fMenuFontHeight, FontStyleRegular);
		StringFormat strfmtLC;
		strfmtLC.SetAlignment(StringAlignmentNear);
		strfmtLC.SetLineAlignment(StringAlignmentCenter);
		strfmtLC.SetTrimming(StringTrimmingEllipsisCharacter);
		Color clrText;
		clrText.SetFromCOLORREF(bDisabled ? m_crDisItemText : m_crItemText);
		SolidBrush brItemText(clrText);
		
		gfx.DrawString(CT2OLE(strText), strText.GetLength(), 
			&fnt, RC2GPRF(rText), &strfmtLC, &brItemText);
	}

	if(bHighlight)
	{
		CRect rHighlight = rItem;
		rHighlight.InflateRect(-1,-1,-1,-1);
		Pen pnHLBorder(Color(255,0,0,0), 1);
		SolidBrush brHighlight(Color(80,0,0,128));
		gfx.FillRectangle(&brHighlight, RC2GPR(rHighlight));
		gfx.DrawRectangle(&pnHLBorder, RC2GPR(rHighlight));
	}
}

void CListCtrlHDOD::OnMeasureItem(int nIDCtl, MEASUREITEMSTRUCT* pMI)
{
	if(pMI->CtlType == ODT_MENU)
	{
		pMI->itemHeight = 24;
		pMI->itemWidth = (m_iMenuItemWidth + 24);
	}

	CListCtrl::OnMeasureItem(nIDCtl, pMI);
}

void CListCtrlHDOD::MeasureItem(MEASUREITEMSTRUCT* pMI)
{
	pMI->itemHeight = m_iItemHeight;
}

//=================================================
// Property Accessor Functions
//=================================================
void CListCtrlHDOD::SetItemHeight(int iHeight)
{
	if(m_iItemHeight == iHeight)
		return;
	m_iItemHeight = iHeight;

	// Force measure item 
	CRect rThis;
	GetWindowRect(&rThis);
	WINDOWPOS wp = {0};
	wp.hwnd = GetSafeHwnd();
	wp.cx = rThis.Width();
	wp.cy = rThis.Height();
	wp.flags = SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER;
	SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);
}

//=================================================
// Implementation
//=================================================
int CListCtrlHDOD::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat, 
	int nWidth, int nSubItem, BOOL bHidden/*=FALSE*/, BOOL bDisableHide/*=FALSE*/,
	BOOL bResizeable/*=TRUE*/, BOOL bSortable/*=TRUE*/, int iMinWidth/*=30*/, 
	CString cstrToolTip/*=_T("")*/)
{
	// Call base class
	int iItem = CListCtrl::InsertColumn(nCol, 
		lpszColumnHeading, nFormat, (bHidden ? 0 : nWidth), nSubItem);
	if(iItem < 0)
		return iItem;

	// Header control is not created until first column is added
	if(!m_HeaderCtrl.GetSafeHwnd())
		VERIFY(m_HeaderCtrl.SubclassDlgItem(0, this));

	// Store column data 
	CColumnData* pColData = new CColumnData;
	ASSERT(pColData);
	pColData->m_bHidden = bHidden;
	pColData->m_bDisableHide = bDisableHide;
	pColData->m_bResizeable = bResizeable;
	pColData->m_bSortable = bSortable;
	pColData->m_iMinWidth = iMinWidth;
	pColData->m_cstrToolTip = cstrToolTip;
	pColData->m_iFormat = nFormat;
	pColData->m_iDefWidth = nWidth;
	pColData->m_iCurWidth = nWidth;

	// Get newly created header item
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	VERIFY(m_HeaderCtrl.GetItem(nCol, &hdi));
	hdi.lParam = (LPARAM)pColData;
	VERIFY(m_HeaderCtrl.SetItem(nCol, &hdi));

	return iItem;
}

BOOL CListCtrlHDOD::DeleteColumn(int nCol)
{
	// Delete header item data
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	VERIFY(m_HeaderCtrl.GetItem(nCol, &hdi));
	CColumnData* pColData = (CColumnData*)hdi.lParam;
	ASSERT(pColData);
	delete pColData;
	return CListCtrl::DeleteColumn(nCol);
}

void CListCtrlHDOD::SortList()
{
	if(!m_pfnCompareFunc)
		return;
	SortItems(m_pfnCompareFunc, (DWORD_PTR)this);
}

//=================================================
// Sort Callback
//=================================================
int CALLBACK CListCtrlHDOD::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CListCtrlHDOD* pThis = (CListCtrlHDOD*)lParamSort;
	ASSERT(pThis);

	LVFINDINFO lvfi1 = {0};
	lvfi1.flags = LVFI_PARAM;
	lvfi1.lParam = lParam1;
	LVFINDINFO lvfi2 = {0};
	lvfi2.flags = LVFI_PARAM;
	lvfi2.lParam = lParam2;
	int iIndex1 = pThis->FindItem(&lvfi1);
	ASSERT(iIndex1 != -1);
	int iIndex2 = pThis->FindItem(&lvfi2);
	ASSERT(iIndex2 != -1);

	CString strItem1 = pThis->GetItemText(iIndex1, pThis->m_iSortCol);
	CString strItem2 = pThis->GetItemText(iIndex2, pThis->m_iSortCol);
	int iResult = _tcscmp(strItem1, strItem2);

	return (pThis->m_bSortAsc ? iResult : (-iResult));
}

</lpnmheader></lpnmheader></lpnmlistview></lpnmheader></lpnmheader>
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900