|
It doesn't work either, because at that point CHeaderCtrl::GetItemCount() returns 0, since normally columns are added in OnInitDialog( ) method.
I don't know how to solve it.
Jaime
|
|
|
|
|
I had the EXACT same situation... OnCreate() doesn't get called. Tried moving it to PreSubClassWindow(), but like you said, GetItemCount() returns 0. I tried putting it in all kinds of different functions that I thought might get called when the control is first being created/displayed, but nothing seems to work! Anyone figure this out yet?
--luke
|
|
|
|
|
Good news... I figured out a workaround for getting multiline headers to work in a CListCtrl object. Unfortunately, with the author's code, you can't use OnCreate() or PreSubClassWindow(). However, you can try the following approach, which in some ways makes your list class more usable because you can turn on/off multiline whenever you feel like it.
In your derived list control class (Example: CMyListCtrl) you need these four member variables in MyListCtrl.h:
bool m_bInit;
CFont m_FontSingle;
CFont m_FontMultiple;
CCtrlListHeaderEx m_HeaderCtrl; // multiline header control
Two of these need to be initialized, so in the constructor of your list class (CMyListCtrl), place the following:
m_bInit = false;
m_FontMultiple.CreatePointFont(190,"MS Serif"); // 190 for two rows of text
You will need the CCtrlListHeaderEx class, which is pretty much a tiny class with only one real function, so i just inline it and put it above the list class in MyListCtrl.h:
// A simple extension to the header class to allow multi-line headers
class CCtrlListHeaderEx : public CHeaderCtrl
{
public:
CCtrlListHeaderEx() { m_bMultiline = false; }
virtual ~CCtrlListHeaderEx() { }
bool m_bMultiline;
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) // override
{
ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
HDITEM hdi;
TCHAR lpBuffer[256];
hdi.mask = HDI_TEXT;
hdi.pszText = lpBuffer;
hdi.cchTextMax = 256;
GetItem(lpDrawItemStruct->itemID, &hdi);
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT)); // this font is only for drawing as long as we don't do a SetFont()
::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH); // Draw the button frame.
UINT uFormat;
if (m_bMultiline) { uFormat = DT_CENTER; } else { uFormat = DT_SINGLELINE | DT_CENTER; }
::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer), &lpDrawItemStruct->rcItem, uFormat); // Draw the text
pDC->SelectStockObject(SYSTEM_FONT);
}
};
It's basically the same class that the author used, except that I added a m_bMultiline variable and some code to switch between one and the other.
Then, override PreSubclassWindow() for CMyListCtrl. The function should contain the following:
void CMyListCtrl::PreSubclassWindow()
{
CHeaderCtrl* pHeader = GetHeaderCtrl();
if (pHeader==NULL) { return; }
VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
CListCtrl::PreSubclassWindow();
}
Then create a function for CMyListCtrl called SetHeaderMultiline() which looks like:
void CMyListCtrl::SetHeaderMultiline(bool bMulti)
{
// Set up multiline headers
InsertItem(0,"DELETE!"); // Used to trick the column bar to refresh
// the following code will only ever get called once in the class's lifetime
if (!m_bInit) {
m_bInit = true;
// get the current font so we can revert back to singleline later if needed
CFont* pOldFont = m_HeaderCtrl.GetFont();
LOGFONT LogFont; pOldFont->GetLogFont(&LogFont);
m_FontSingle.CreateFontIndirect(&LogFont);
// set ownerdraw to true (unfortunately we have to do this here because
// there doesn't seem to be an init method that we can override
// since OnCreate() doesn't get called for CListCtrl derived classes
// and GetItemCount() returns 0 within PreSubclassWindow()
HDITEM hdItem; hdItem.mask = HDI_FORMAT;
for (int i = 0; i < m_HeaderCtrl.GetItemCount(); i++) {
m_HeaderCtrl.GetItem(i,&hdItem);
hdItem.fmt |= HDF_OWNERDRAW;
m_HeaderCtrl.SetItem(i,&hdItem);
}
}
//bool bFlipOwnerDraw = false;
if (bMulti && !m_HeaderCtrl.m_bMultiline) {
m_HeaderCtrl.SetFont(&m_FontMultiple);
m_HeaderCtrl.m_bMultiline = true;
} else if (m_HeaderCtrl.m_bMultiline) {
m_HeaderCtrl.SetFont(&m_FontSingle);
m_HeaderCtrl.m_bMultiline = false;
}
DeleteItem(0); // Completing the trick requires removing the grabage!
}
At this point your derived CMyListCtrl class should have a nifty new function called
SetHeaderMultiline(...); Now, all you have to do is call that function from in the class which contains the list. A good example might be to call this from OnInitDialog() in a dialog box.
m_List.SetHeaderMultiline(true);
It sounds more complicated than it is... just add those 4 variables, initialize two of them in the list's constructor, define the CCtrlListHeaderEx class, and paste in the SetHeaderMultiline() function -- and your derived list control class should be displaying multiline headers in no time.
questions/comments: lclemens@gmail.com
--luke
|
|
|
|
|
|
Hi,
It Works fine in my Dummy Application but when used in the same way in my actual application, giving Assertion from the line:
VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
To be more precise, assertion comes from the line:
ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());
of the method: BOOL CWnd::SubclassWindow(HWND hWnd)
Any idea of how to over come this problem?!?! (Thanks in advance!!)
=====
How did I try:
With Visual Studio frist I dropped a ListCtrl in a dialog, and associated it to a variable m_ListCtrl. Then in OnInitDialog() method I just copy pasted the content of the method void CHeaderCtrlExView::OnInitialUpdate(), with the following modifications:
1. removed the first two lines:
CListView::OnInitialUpdate();
CListCtrl& ListCtrl = GetListCtrl();
2. replaced ListCtrl with m_ListCtrl.
3. copied your code for // insert columns
4. copied your code for //SET UP THE MULTILINE HEADER CONTROL
5. copied your code for // filling rows/items.
sg raman (sg_raman@yahoo.com)
|
|
|
|
|
What I would do in this case is instead of dropping the List Control in the dialog and using it the way you did, create the List Control on the dialog and then go on as showed in the example.
Hope this helps.
Cheers!
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
thank u again
|
|
|
|
|
Never tried this. Try it and see what happens. All you neet to do is change the LOGFONT.
Hey if it is possible let me know. I'm just curious
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
you code did not check '\' char at all.
how can ::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer),
&lpDrawItemStruct->rcItem, uFormat);
fuction divide the chars in two parts and show them separately in two line?
such as
Color\nName to
color
nName
i am puzzled with that, could u explain any?
|
|
|
|
|
Look for the _gszColumnLabel array and you will see the answer there.
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
msn uses a new HeaderCtrl to make it looks clean(maybe use it to subclass listview's headerctrl),no ugly background color of headerctrl.
also i don't know how can they show image in subitems while using report style.
could u be kindly tell me how to make listview looks like msn's.
thanx so much.
|
|
|
|
|
I wish I knew...
I would bet on owner draw all the way... this way you can do everytjing you want. Now I am sure this is not what you wanted to "hear" (read ) but alas this is what I would do...
Good luck!
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
thank u.
i do it with this class derived from CListCtrl
#if !defined(AFX_XLISTCTRL_H__315B1D36_F462_4D48_AA35_F5C80F8FA2C4__INCLUDED_)
#define AFX_XLISTCTRL_H__315B1D36_F462_4D48_AA35_F5C80F8FA2C4__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// XListCtrl.h : header file
//
#include "AllFriends.h"
struct XLISTCTRLDATA
{
// ctor
XLISTCTRLDATA()
{
bEnabled = TRUE;
crText = ::GetSysColor(COLOR_WINDOWTEXT);
crBackground = ::GetSysColor(COLOR_WINDOW);
bShowProgress = FALSE;
nProgressPercent = 0;
strProgressMessage = _T("");
bShowProgressMessage = TRUE;
nCheckedState = -1;
bBold = FALSE;
nImage = -1;
#ifndef NO_XLISTCTRL_TOOL_TIPS
strToolTip = _T("");
#endif
bCombo = FALSE;
bComboIsClicked = FALSE;
nComboListHeight = 10;
nInitialComboSel = 0;
psa = NULL;
dwItemData = 0;
}
BOOL bEnabled; // TRUE = enabled, FALSE = disabled (gray text)
BOOL bBold; // TRUE = display bold text
int nImage; // index in image list, else -1
#ifndef NO_XLISTCTRL_TOOL_TIPS
CString strToolTip; // tool tip text for cell
#endif
// for combo
BOOL bCombo; // TRUE = display combobox
BOOL bComboIsClicked; // TRUE = downarrow is clicked
CStringArray * psa; // pointer to string array for combo listbox
int nComboListHeight; // combo listbox height (in rows)
int nInitialComboSel; // initial combo listbox selection (0 = first)
// for color
COLORREF crText;
COLORREF crBackground;
// for progress
BOOL bShowProgress; // true = show progress control
int nProgressPercent; // 0 - 100
CString strProgressMessage; // custom message for progress indicator -
// MUST INCLUDE %d
BOOL bShowProgressMessage; // TRUE = display % message, or custom message
// if one is supplied
// for checkbox
int nCheckedState; // -1 = don't show, 0 = unchecked, 1 = checked
DWORD dwItemData; // pointer to app's data
};
/////////////////////////////////////////////////////////////////////////////
// CXListCtrl window
class CXListCtrl : public CListCtrl
{
// Construction
public:
CXListCtrl();
// Attributes
public:
CImageList m_cImageList;
CAllFriends m_allfrds;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CXListCtrl)
//}}AFX_VIRTUAL
// Implementation
public:
int InsertItem(int nItem, LPCTSTR lpszItem);
int InsertItem(int nItem,
LPCTSTR lpszItem,
COLORREF crText,
COLORREF crBackground);
BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpszText,
COLORREF crText, COLORREF crBackground);
int GetColumns();
void UpdateSubItem(int nItem, int nSubItem);
BOOL SetItemImage(int nItem, int nSubItem, int nImage);
virtual ~CXListCtrl();
public:
void BuildList();
// COLORREF m_crGrayText;
COLORREF m_crWindow;
COLORREF m_crWindowText;
COLORREF m_crBtnFace;
COLORREF m_crHighLight;
COLORREF m_crHighLightText;
// Generated message map functions
protected:
void DrawText(int nItem,
int nSubItem,
CDC *pDC,
COLORREF crText,
COLORREF crBkgnd,
CRect& rect,
XLISTCTRLDATA *pXLCD);
int DrawImage(int nItem,
int nSubItem,
CDC* pDC,
COLORREF crText,
COLORREF crBkgnd,
CRect rect,
XLISTCTRLDATA *pXLCD);
void GetDrawColors(int nItem,
int nSubItem,
COLORREF& colorText,
COLORREF& colorBkgnd);
protected:
//{{AFX_MSG(CXListCtrl)
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnClick(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnRclick(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_XLISTCTRL_H__315B1D36_F462_4D48_AA35_F5C80F8FA2C4__INCLUDED_)
NOW .cpp FILE
// XListCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "xclient.h"
#include "XListCtrl.h"
#include "group.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "contact.h"
/////////////////////////////////////////////////////////////////////////////
// CXListCtrl
CXListCtrl::CXListCtrl()
{
m_crBtnFace = ::GetSysColor(COLOR_BTNFACE);
m_crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
m_crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_crWindow = ::GetSysColor(COLOR_WINDOW);
m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
// VERIFY(m_cImageList.Create(IDB_BITMAP2, 16, 7, RGB(0, 128, 128)));
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP2);
m_cImageList.Create(16, 16, ILC_COLOR16|ILC_MASK, 4, 1);
m_cImageList.Add(&bitmap, RGB(0,128,128));
}
CXListCtrl::~CXListCtrl()
{
}
BEGIN_MESSAGE_MAP(CXListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CXListCtrl)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
ON_NOTIFY_REFLECT(NM_RCLICK, OnRclick)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CXListCtrl message handlers
BOOL CXListCtrl::SetItemImage(int nItem, int nSubItem, int nImage)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return FALSE;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return FALSE;
BOOL rc = TRUE;
if (nItem < 0)
return FALSE;
XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
if (pXLCD)
{
pXLCD[nSubItem].nImage = nImage;
}
UpdateSubItem(nItem, nSubItem);
return rc;
}
void CXListCtrl::UpdateSubItem(int nItem, int nSubItem)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return;
CRect rect;
if (nSubItem == -1)
{
GetItemRect(nItem, &rect, LVIR_BOUNDS);
}
else
{
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
}
InvalidateRect(&rect);
UpdateWindow();
}
int CXListCtrl::GetColumns()
{
return GetHeaderCtrl()->GetItemCount();
}
void CXListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<nmlvcustomdraw*>(pNMHDR);
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
{
// This is the notification message for an item. We'll request
// notifications before each subitem's prepaint stage.
*pResult = CDRF_NOTIFYSUBITEMDRAW;
}
else if (pLVCD->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
{
// This is the prepaint stage for a subitem. Here's where we set the
// item's text and background colors. Our return value will tell
// Windows to draw the subitem itself, but it will use the new colors
// we set here.
int nItem = static_cast<int> (pLVCD->nmcd.dwItemSpec);
int nSubItem = pLVCD->iSubItem;
XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) pLVCD->nmcd.lItemlParam;
ASSERT(pXLCD);
COLORREF crText = m_crWindowText;
COLORREF crBkgnd = m_crWindow;
if (pXLCD)
{
crText = pXLCD[nSubItem].crText;
crBkgnd = pXLCD[nSubItem].crBackground;
// if (!pXLCD[0].bEnabled)
// crText = m_crGrayText;
}
// store the colors back in the NMLVCUSTOMDRAW struct
pLVCD->clrText = crText;
pLVCD->clrTextBk = crBkgnd;
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rect;
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
if (pXLCD && (pXLCD[nSubItem].bShowProgress))
{
//DrawProgress(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
#ifndef DO_NOT_INCLUDE_XCOMBOLIST
else if (pXLCD && (pXLCD[nSubItem].bCombo))
{
DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
#endif
else if (pXLCD && (pXLCD[nSubItem].nCheckedState != -1))
{
//DrawCheckbox(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
else
{
rect.left += DrawImage(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect, pXLCD);
*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
}
}
int CXListCtrl::DrawImage(int nItem,
int nSubItem,
CDC* pDC,
COLORREF crText,
COLORREF crBkgnd,
CRect rect,
XLISTCTRLDATA *pXLCD)
{
GetDrawColors(nItem, nSubItem, crText, crBkgnd);
pDC->FillSolidRect(&rect, crBkgnd);
int nWidth = 0;
//rect.left += 6;//m_HeaderCtrl.GetSpacing(); //look and see
CImageList* pImageList = GetImageList(LVSIL_SMALL);
if (pImageList)
{
SIZE sizeImage;
sizeImage.cx = sizeImage.cy = 0;
IMAGEINFO info;
int nImage = -1;
if (pXLCD)
nImage = pXLCD[nSubItem].nImage;
if (nImage == -1)
return 0;
if (pImageList->GetImageInfo(nImage, &info))
{
sizeImage.cx = info.rcImage.right - info.rcImage.left;
sizeImage.cy = info.rcImage.bottom - info.rcImage.top;
}
if (nImage >= 0)
{
if (rect.Width() > 0)
{
POINT point;
point.y = rect.CenterPoint().y - (sizeImage.cy >> 1);
point.x = rect.left;
SIZE size;
size.cx = rect.Width() < sizeImage.cx ? rect.Width() : sizeImage.cx;
size.cy = rect.Height() < sizeImage.cy ? rect.Height() : sizeImage.cy;
// save image list background color
COLORREF rgb = pImageList->GetBkColor();
// set image list background color
pImageList->SetBkColor(crBkgnd);
pImageList->DrawIndirect(pDC, nImage, point, size, CPoint(0, 0));
pImageList->SetBkColor(rgb);
nWidth = sizeImage.cx + 6;//m_HeaderCtrl.GetSpacing();
}
}
}
return nWidth;
}
void CXListCtrl::GetDrawColors(int nItem,
int nSubItem,
COLORREF& colorText,
COLORREF& colorBkgnd)
{
DWORD dwStyle = GetStyle();
DWORD dwExStyle = GetExtendedStyle();
COLORREF crText = colorText;
COLORREF crBkgnd = colorBkgnd;
if (GetItemState(nItem, LVIS_SELECTED))
{
if (dwExStyle & LVS_EX_FULLROWSELECT)
{
// selected? if so, draw highlight background
crText = m_crHighLightText;
crBkgnd = m_crHighLight;
// has focus? if not, draw gray background
if (m_hWnd != ::GetFocus())
{
if (dwStyle & LVS_SHOWSELALWAYS)
{
crText = m_crWindowText;
crBkgnd = m_crBtnFace;
}
else
{
crText = colorText;
crBkgnd = colorBkgnd;
}
}
}
else // not full row select
{
if (nSubItem == 0)
{
// selected? if so, draw highlight background
crText = m_crHighLightText;
crBkgnd = m_crHighLight;
// has focus? if not, draw gray background
if (m_hWnd != ::GetFocus())
{
if (dwStyle & LVS_SHOWSELALWAYS)
{
crText = m_crWindowText;
crBkgnd = m_crBtnFace;
}
else
{
crText = colorText;
crBkgnd = colorBkgnd;
}
}
}
}
}
colorText = crText;
colorBkgnd = crBkgnd;
}
void CXListCtrl::DrawText(int nItem,
int nSubItem,
CDC *pDC,
COLORREF crText,
COLORREF crBkgnd,
CRect& rect,
XLISTCTRLDATA *pXLCD)
{
ASSERT(pDC);
ASSERT(pXLCD);
GetDrawColors(nItem, nSubItem, crText, crBkgnd);
pDC->FillSolidRect(&rect, crBkgnd);
CString str;
str = GetItemText(nItem, nSubItem);
if (!str.IsEmpty())
{
// get text justification
HDITEM hditem;
hditem.mask = HDI_FORMAT;
GetHeaderCtrl()->GetItem(nSubItem, &hditem); //modified!! m_HeaderCtrl.GetItem(nSubItem, &hditem);
int nFmt = hditem.fmt & HDF_JUSTIFYMASK;
UINT nFormat = DT_VCENTER | DT_SINGLELINE;
if (nFmt == HDF_CENTER)
nFormat |= DT_CENTER;
else if (nFmt == HDF_LEFT)
nFormat |= DT_LEFT;
else
nFormat |= DT_RIGHT;
CFont *pOldFont = NULL;
CFont boldfont;
// check if bold specified for subitem
if (pXLCD && pXLCD[nSubItem].bBold)
{
CFont *font = pDC->GetCurrentFont();
if (font)
{
LOGFONT lf;
font->GetLogFont(&lf);
lf.lfWeight = FW_BOLD;
boldfont.CreateFontIndirect(&lf);
pOldFont = pDC->SelectObject(&boldfont);
}
}
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(crText);
pDC->SetBkColor(crBkgnd);
pDC->DrawText(str, &rect, nFormat);
if (pOldFont)
pDC->SelectObject(pOldFont);
}
}
BOOL CXListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszText,
COLORREF crText, COLORREF crBackground)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return FALSE;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return FALSE;
BOOL rc = TRUE;
if (nItem < 0)
return FALSE;
if (lpszText)
rc = CListCtrl::SetItemText(nItem, nSubItem, lpszText);
XLISTCTRLDATA *pXLCD = (XLISTCTRLDATA *) CListCtrl::GetItemData(nItem);
if (pXLCD)
{
pXLCD[nSubItem].crText = (crText == -1) ? m_crWindowText : crText;
pXLCD[nSubItem].crBackground = (crBackground == -1) ? m_crWindow : crBackground;
}
UpdateSubItem(nItem, nSubItem);
return rc;
}
int CXListCtrl::InsertItem(int nItem, LPCTSTR lpszItem)
{
ASSERT(nItem >= 0);
if (nItem < 0)
return -1;
return InsertItem(nItem,
lpszItem,
m_crWindowText,
m_crWindow);
}
///////////////////////////////////////////////////////////////////////////////
// InsertItem
int CXListCtrl::InsertItem(int nItem,
LPCTSTR lpszItem,
COLORREF crText,
COLORREF crBackground)
{
ASSERT(nItem >= 0);
if (nItem < 0)
return -1;
int index = CListCtrl::InsertItem(nItem, lpszItem);
if (index < 0)
return index;
XLISTCTRLDATA *pXLCD = new XLISTCTRLDATA [GetColumns()];
ASSERT(pXLCD);
if (!pXLCD)
return -1;
pXLCD[0].crText = crText;
pXLCD[0].crBackground = crBackground;
pXLCD[0].nImage = -1;
CListCtrl::SetItemData(index, (DWORD) pXLCD);
return index;
}
int CXListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
CHeaderCtrl* m_phead=GetHeaderCtrl();
SetImageList(&m_cImageList, LVSIL_SMALL);
ModifyStyle(NULL,LVS_NOCOLUMNHEADER | WS_VSCROLL | WS_VISIBLE);
SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES |WS_EX_RIGHTSCROLLBAR);
InsertColumn(0, "nikename", LVCFMT_LEFT, 22,0);
InsertColumn(1, "", LVCFMT_LEFT, 200,1);
BuildList();
ShowWindow(SW_SHOW);
return 0;
}
|
|
|
|
|
Hi skygg,
I'm trying to code a "user list" like msn does. Do you have any example project that can teach me how to do it?. It looks your CXListCtrl does it, but I don't know how to use it into a project.
Thanks,
toni
|
|
|
|
|
I use a tab windows which consists of 5 child dialogs.In one of child dialogs,i put a list ctrl.Once i use code like
"ASSERT(m_HeaderCtrl.SubclassWindow(m_pHeader->m_pHwnd)"
No header will disappear.How can i change the code to avoid this error.Tell me please.Thanks in advance.
|
|
|
|
|
sages wrote:
No header will disappear
I am not sure what you mean by that and it actually confuses me...
As for the code... make sure the m_pHeader variable actually is the one you need. I have never tried this code the way you do it so I can't figure out what could go wrong.
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
Sorry to confuse u.
I put a listctrl in one dialog and would like to display multiheader in the listctrl. The dialog is in a Tab Window.When i call your codes, the header of listctrl does not show,as if i didn't set header.Once i delete
"ASSERT(m_HeaderCASSERT(m_HeaderCtrl.SubclassWindow)"
The header will show without muliti header. As a result,i think it's because of this line.I can NOT know how to change this line code to avoid the error.My window child-parent construct is:
(listctrl parent wnd-->dialog parent wnd-->tab window wnd)
Thank u in advance.
|
|
|
|
|
OK I'm stuck.... Do you mind sending me an example project at galberto@itgil.com
Cheers!
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|
Thank u very much.I've found the reason.It's a little bug.The var scope defined is too small.However,There's a new problem.How can i change the height of header?
|
|
|
|
|
I was very happy to find and use this technique for my custom listctrl. When I switched to dotnet, however I found that it did not work properly.
Changing the font in the header no longer seemed to affect the size of the header. I tried drawing the header manually, but then I still had the problem with the rows in the 'view" rect being covered up by the bottom of the newly drawn header.
I found the solution to both problems is to subclass the header and handle the HDM_LAYOUT message. (This can be done with the ON_MESSAGE macro).
The LPARAM of this message contains a HDLAYOUT structure, which contains a RECT and WINDOWPOS. The RECT will contain the overall size of the listctrl, and WINDOWPOS will return the correct coord for the header.
The listctrl, when sizing, sends this message to the header, with a RECT containing the overall size of itself, say for example, RECT(0,0,100,100). The header ctrl, takes these measurements, calulates and returns the appropriate size of the header in a WINDOWPOS structure. It also adjusts the top member of the passed rect from 0 to the value the 'view rect' of the list part should be. eg returns RECT(0,xx,100,100) in the rect structure.
BEGIN_MESSAGE_MAP(cListctrl_header, CHeaderCtrl)
ON_NOTIFY_REFLECT( NM_CUSTOMDRAW, OnCustomDraw )
ON_MESSAGE( HDM_LAYOUT, on_layout )
END_MESSAGE_MAP()
.
.
.
LRESULT cListctrl_header::on_layout( WPARAM wParam, LPARAM lParam )
{
CClientDC dc(this);
CSize size = dc.GetTextExtent( "test" );
HDLAYOUT* pLayout = (HDLAYOUT*) lParam; // cast the lParam
pLayout->pwpos->hwnd = GetSafeHwnd(); // populate WINDOWPOS struct
pLayout->pwpos->hwndInsertAfter = NULL;
pLayout->pwpos->flags = SWP_FRAMECHANGED;
pLayout->pwpos->x = pLayout->prc->left;
pLayout->pwpos->y = 0;
pLayout->pwpos->cx = pLayout->prc->right - pLayout->prc->left;
// I have a fcn, get_hdr_line_count() that returns the # of lines in hdr
// you can substitute an int (eg 2) for this fcn
int btm_of_hdr =
(((cListCtrl_ex*)GetParent())->get_header_line_count())*(size.cy)+3;
pLayout->pwpos->cy = btm_of_header;
pLayout->prc->top = btm_of_header; //start the row positions correctly
return -1;
}
Mitch Haas
|
|
|
|
|
This is finally the correct approach!
Well discovered!
|
|
|
|
|
void CHeaderCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
HDITEM hdi;
TCHAR lpBuffer[256];
hdi.mask = HDI_TEXT | HDI_FORMAT;
hdi.pszText = lpBuffer;
hdi.cchTextMax = 256;
GetItem(lpDrawItemStruct->itemID, &hdi);
UINT uFormat = DT_CENTER;
CDC* pDC;
pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
//calculate text rectangle first
CRect tmpRect = lpDrawItemStruct->rcItem;
::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer),
&tmpRect, DT_CALCRECT|DT_WORDBREAK);
if (tmpRect.bottom > lpDrawItemStruct->rcItem.bottom)
lpDrawItemStruct->rcItem.bottom = tmpRect.bottom;
//THIS FONT IS ONLY FOR DRAWING AS LONG AS WE DON'T DO A SetFont(...)
pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT));
// Draw the button frame.
::DrawFrameControl(lpDrawItemStruct->hDC,
&lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
//DRAW THE TEXT
::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer),
&lpDrawItemStruct->rcItem, uFormat|DT_WORDBREAK);
pDC->SelectStockObject(SYSTEM_FONT);
}
lodo
|
|
|
|
|
I got an email response to this article as follows:
Adam,
I am pulling my hair out with this code. It looks very cool, but I can't get it to work in a CFormView. By chance, could you help me put together a demo project of this multiline header control for a list control in a CFormView rather than the CListView? Please, please.
Any response you can give me will be greatly appreciated.
Sincerely,
**Censored**
I thought sure... no problems... and here is what I did...
I overwrote List control and added a single function:
void MLHListCtrl::InitControlHeadings(CStringArray &myColumnHeaders)
{
// Here I figure out the column sizes and populate the columns
RECT rect;
GetClientRect(&rect);
int colWidth = (int)(rect.right - rect.left)/myColumnHeaders.GetSize();
for(int i = 0; i < myColumnHeaders.GetSize(); i++)
InsertColumn(i,myColumnHeaders.GetAt(i),LVCFMT_CENTER,colWidth);
// **** SET UP MULTILINE HEADERS **** \/
InsertItem(0,"DELETE!"); // Used to trick the column bar to refresh
CHeaderCtrl* pHeader = NULL;
pHeader = GetHeaderCtrl();
if(pHeader==NULL) return;// FALSE;
VERIFY(m_ctrlHeader.SubclassWindow(pHeader->m_hWnd));
//m_NewHeaderFont is of type CFont
m_cfontNewHeaderFont.CreatePointFont(390,"MS Serif"); //190 for two rows of text
m_ctrlHeader.SetFont(&m_cfontNewHeaderFont);
HDITEM hdItem;
hdItem.mask = HDI_FORMAT;
for(i=0; i < m_ctrlHeader.GetItemCount(); i++)
{
m_ctrlHeader.GetItem(i,&hdItem);
hdItem.fmt|= HDF_OWNERDRAW;
m_ctrlHeader.SetItem(i,&hdItem);
}
DeleteItem(0); // Completing the trick requires removing the grabage!
// **** SET UP MULTILINE HEADERS **** ^
}
Note that I had private member vairables:
CFont m_cfontNewHeaderFont;
CHeaderCtrlEx m_ctrlHeader;
Then in the dialog, I added a handler for WM_INITDIALOG and then did the following:
CStringArray ColumnHeader;
ColumnHeader.Add("Column 1\nLine 1\nLine 2\nLine 3\nLine 4");
ColumnHeader.Add("Column 2\nLine 1\nLine 2\nLine 3\nLine 4");
ColumnHeader.Add("Column 3\nLine 1\nLine 2\nLine 3\nLine 4");
ColumnHeader.Add("Column 4\nLine 1\nLine 2\nLine 3\nLine 4");
myList.InitControlHeadings(ColumnHeader);
Where myList is a member variable for my overwritten control.
This is just a quick hack, probably could make it nicer, but just wanted to show how easy it was to use this class in the control as well as the view.
Hope this is useful.
Cheers,
Adam
|
|
|
|
|
I use OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor),with
nCtlColor== CTLCOLOR_LISTBOX and draw,but it didn't work,how can I do this?
I'm a beginner, please explain it clearly,thanks.
fyl_jf
|
|
|
|
|
Hi there,
Try this article : http://www.codeproject.com/listctrl/lvcustomdraw.asp
Cheers!!
Alberto Gattegno
Software Engineer
http://www.itgil.com
|
|
|
|
|