Solved

Need some help with CListCtrl and color change

Posted on 2008-10-20
19
1,167 Views
Last Modified: 2013-11-20
I need some help with my ClistCtrl.
I have created a new Class from ClistCtrl to change the cursor when
I am over a text in column/row.
Everything works just fine. The cursor changes over a text in column 4/6/7/8
when there is text in a row (cell)
I now want to change the color of the text when the mouse is over and like
a hyperlink.

I my programm Ckontaktpersonen I have a OnCustomDrawList where I change some
color and also add bitmaps to some rows.

How can I do this. I think I have all the code&.I just need some help.
500 Points Grade A with a solution

To add just a variable like m_bMouseOverText(false) will not work.

Best regards,
Thomas
500 points grade A with a solution




//##############################################################
//--------------------- Color CListCtrl ---------------------------------
 
void CKontaktpersonen::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult)
 {
       NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );

    // Take the default processing unless we set this to something else below.
    *pResult = 0;

    // First thing - check the draw stage. If it's the control's prepaint
    // stage, then tell Windows we want messages for every item.

    if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
        {
        *pResult = CDRF_NOTIFYITEMDRAW;
        }
    else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
        {
        // This is the notification message for an item.  We'll request
        // notifications before each subitem's prepaint stage.

        *pResult = CDRF_NOTIFYSUBITEMDRAW;
        }
    else if ( (CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage )
        {
        // 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.
       
   
            COLORREF crText, crBkgnd;

            

                  //-------------- rows in color ----------------------
             

                  int iCol = pLVCD->iSubItem;
                  int iRow = pLVCD->nmcd.dwItemSpec;

                  if(m_List_Sonder.m_bMouseOverText==true)
                  {
                        crText = RGB(255,0,0);      //red
                    crBkgnd= RGB(240,255,255);

            }

            

                  CString str=m_List.GetItemText(iRow,1);

                  if(iCol==1 && str=="w")
                  {
                        crText = RGB(255,0,0);      //red
                    crBkgnd= RGB(240,255,255);

            }
                  else
                  {
                         crText = RGB(0,0,255);      //blueu  
                     crBkgnd= RGB(240,255,255);
                  }
                  
           //----------------------------------------------------
                  
        
          // Store the colors back in the NMLVCUSTOMDRAW struct.
        pLVCD->clrText = crText;
        pLVCD->clrTextBk = crBkgnd;

        // Tell Windows to paint the control itself.
        *pResult = CDRF_DODEFAULT;
        }
             
}
 
 //#############################################################



// MouseOverListCtrl_Sonder.cpp : Implementierungsdatei
//

#include "stdafx.h"
#include "Stockbruegger.h"
#include "MouseOverListCtrl_Sonder.h"
#include ".\mouseoverlistctrl_sonder.h"


// CMouseOverListCtrl_Sonder

IMPLEMENT_DYNAMIC(CMouseOverListCtrl_Sonder, CListCtrl)
CMouseOverListCtrl_Sonder::CMouseOverListCtrl_Sonder()
: m_bMouseOverText(false)
{
}

CMouseOverListCtrl_Sonder::~CMouseOverListCtrl_Sonder()
{
}


BEGIN_MESSAGE_MAP(CMouseOverListCtrl_Sonder, CListCtrl)
      ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()



// CMouseOverListCtrl_Sonder-Meldungshandler


//###########################################################
//###########################################################
void CMouseOverListCtrl_Sonder::OnMouseMove(UINT nFlags, CPoint point)
{
      //------------------ Row and Column CListCtrl ------------------------
      static LVHITTESTINFO info;
      info.pt = point;
      info.flags = LVHT_ONITEM||LVHT_ONITEMLABEL;
      //info.flags= LVHT_ONITEMLABEL;
      if ( SubItemHitTest ( &info ) == -1 )
      {
            // Error occured whilst obtaining the sub item
            return;
      }
      int nItem = info.iItem;         //row
      int nSubItem = info.iSubItem;   //column
    //----------------------------------------------------------------------
      

    //---------------------------- New Cursor ----------------------------
      static CWinApp* pApp = AfxGetApp();
      static HCURSOR my_new_cursor;
      static HCURSOR hOldCursor = GetCursor();      // Store the current cursor for later use
      
      CString str=GetItemText(nItem,nSubItem);

       if (nSubItem ==4 && str!="")//-------------------------------Phone
      {
             my_new_cursor = pApp->LoadCursor(IDC_CURSOR2);
            m_bMouseOverText=true;<---------------------------------???will not work
            
      }
    else if (nSubItem ==6 && str!="")//---------------------------Cell
      {
             my_new_cursor = pApp->LoadCursor(IDC_CURSOR2);
      }
      else if (nSubItem ==7 && str!="")//----------------------------Alternativ Telefon
      {
             my_new_cursor = pApp->LoadCursor(IDC_CURSOR2);
      }
      else if (nSubItem ==9 && str!="")//----------------------------Email
      {
             my_new_cursor = pApp->LoadCursor(IDC_CURSOR2);
      }
      else
      {
            // Set the default arrow cursor
            my_new_cursor = hOldCursor;
            m_bMouseOverText=false;
      }

       ::SetCursor ( my_new_cursor );
      //-----------------------------------------------------------------------



      CListCtrl::OnMouseMove(nFlags, point);
}
//##############################################################




0
Comment
Question by:tsp2002
  • 11
  • 8
19 Comments
 
LVL 31

Expert Comment

by:Zoppo
ID: 22755703
Hi tsp2002

first I think using just a bool is not enough, IMO you need to remember the col/row of the cell the mouse is on, because otherwise a general redraw would cause every entry to be drawn with the color.

So add two 'int' as member instead, set them to -1 in ctor. Then in OnMouseMove set them to current col/row value to change the color or to -1 to reset the color.

Now in 'OnCustomdrawList' check if the item passed to draw is that one you set in the OnMouseMove.

Then you need to redraw the cell you want to change the color. To do this you can use CListCtrl::GetSubItemRect' to retrieve the rectangle to redraw, then call CWnd::Invalidate or CWnd::RedrawWindow to cause the cell being redrawn. Use this everytime the selected item changes.

Then in the

Hope that helps,

ZOPPO
0
 

Author Comment

by:tsp2002
ID: 22755992
Now in 'OnCustomdrawList' check if the item passed to draw is that one you set in the OnMouseMove.
I don´t get any value in OnCustomdrawList. I also get  no true or false with my
bool variable.
 Take a look at my header. What did I miss
CMouseOverListCtrl_Sonder      m_List_Sonder;
 

//Kontaktpersonen.h
#pragma once
#include "afxcmn.h"
#include "auftraege.h"
#include "MouseOverListCtrl_Sonder.h"
 
 
// CKontaktpersonen-Dialogfeld
 
class CKontaktpersonen : public CDialog
{
	DECLARE_DYNAMIC(CKontaktpersonen)
 
public:
	CKontaktpersonen(CWnd* pParent = NULL);   // Standardkonstruktor
	virtual ~CKontaktpersonen();
 
// Dialogfelddaten
	enum { IDD = IDD_KONTAKTPERSONEN };
 
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV-Unterstützung
 
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnOK();
	afx_msg void OnBnClickedOk();
	afx_msg void OnCancel();
	afx_msg void OnBnClickedCancel();
	afx_msg void OnClose();
	virtual BOOL OnInitDialog();
	CMouseOverListCtrl_Sonder m_List;
	
 
	CString AdressNr;
	void Kontaktpersonen_Suchen(void);
private:
	CAuftraege m_rsRecSet;
	IADORecordBinding* m_piAdoRecordBinding;
	_RecordsetPtr m_ptrRs;
	CString m_strConnection;
	CString m_strCmdText;
public:
	void GenerateError(HRESULT hr, PWSTR pwszDescription);
	afx_msg void OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnCustomdrawList(NMHDR*, LRESULT*);
	int iSpalte;
	int iReihe;
 
	CImageList m_image;
	
	CBitmap m_bitmap;
	int m_bitmapIndex;
	int nColumnCount;
	
	
	CMouseOverListCtrl_Sonder      m_List_Sonder;
};
	
 
 
//-------------- Rows in color ----------------------
		 
 
			int iCol = pLVCD->iSubItem;
			int iRow = pLVCD->nmcd.dwItemSpec;
 
			 
			if(m_List_Sonder.m_bMouseOverText==true)
			{
				crText = RGB(255,0,0);      //rot
		        crBkgnd= RGB(240,255,255);
 
            }

Open in new window

0
 

Author Comment

by:tsp2002
ID: 22756057
with Invalidate() I get a value with my bool variable on CustomDraw.
How do I have to call CListCtrl::GetSubItemRect'  in my OnMouseMove?
Because now the hole CListCtrl is redrawn. That looks not nice.
0
Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

 
LVL 31

Expert Comment

by:Zoppo
ID: 22756097
Hi tsp2002,

in OnMouseMove after you retrieved nItem and nSubItem do something like this:

> CRect rect;
> if ( FALSE != GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, &rect ) )
> {
>  InvalidateRect( &rect );
> }

Hope that helps,

ZOPPO
0
 

Author Comment

by:tsp2002
ID: 22756121

Konvertierung des Parameters 4 von 'CRect *__w64 ' in 'CRect &' nicht möglich
I got this error. Converting of Parameter 4 von 'CRect *__w64 ' in 'CRect &' is not
possible
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22756160
Ah, sorry - a mistype - the rect is passed per reference, not as pointer, so it's:

> GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect )

ZOPPO
0
 

Author Comment

by:tsp2002
ID: 22756172
GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect )
I just tried this...will redraw the hole CListCtrl.
Take a look at my hardcopy. I think that are the  values of the complete CListCtrl
 

aaa.BMP
0
 

Author Comment

by:tsp2002
ID: 22756185
sorry...did the breakpoint wrong.
but will redraw hole ListView

aaa.BMP
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22756187
Please check the rect when you place a breakpoint in the line where InvalidateRect is called.
0
 

Author Comment

by:tsp2002
ID: 22756225
sorry I did a mistake....will try something...I think it works
0
 

Author Comment

by:tsp2002
ID: 22756244
it works now....but you are right with the bool variable.
I will try now 2 int to change the color....will you let you know shorly, thanks
0
 

Author Comment

by:tsp2002
ID: 22756662
will not get the value of the 2 variables.
What can I do?

  //---------------------------- New Cursor ----------------------------
	static CWinApp* pApp = AfxGetApp();
	static HCURSOR my_new_cursor;
	static HCURSOR hOldCursor = GetCursor();	// Store the current cursor for later use
	
	CString str=GetItemText(nItem,nSubItem);
 
 	if (nSubItem ==4 && str!="")//-------------------------------Phone
	{
       	my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); 
		 
		m_iColumn=info.iSubItem;
	    m_iRow   =info.iItem;
 
		UpdateData(true);
	    CRect rect;
        if ( FALSE != GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect ) )
       {
            InvalidateRect( &rect );
       }

Open in new window

aaa.BMP
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22756732
Hi,

I don't really understand - where do you have the problem? As far as I can see in your code the 'm_iColumn' and 'm_iRow' should be set correctly since they are set the same way as 'nItem' and 'nSubItem'.

So I guess you mean 'x' and 'y' in 'OnCustomdrawList' - what are the values of these? It might be better to set the breakpoint within the 'if (m_List.m_bMouseOverText==true)' block (so 2 lines below the breakpoint in the screenshot). Then take a look what 'x' and 'y' are ...

ZOPPO
0
 

Author Comment

by:tsp2002
ID: 22756772

In the header of CKontaktpersonen I had enclosed
CMouseOverListCtrl_Sonder m_List_Sonder;
 
In CustomDraw of CKontaktpersonen
I have no value of m_iColumn and m_iRow
I think I need something like UpdateData for my variables????
Please help, thank you.
 
//-------------- Rows in color ----------------------

int iCol = pLVCD->iSubItem;
int iRow = pLVCD->nmcd.dwItemSpec;
CString str=m_List.GetItemText(iRow,1);

int x= m_List_Sonder.m_iColumn; <------------------------------------------------
int y= m_List_Sonder.m_iRow; <----------------------------------------------------
 
if(iCol==x && iRow==y)
{
crText = RGB(255,0,0); //rot
crBkgnd= RGB(240,255,255);
}

#pragma once
 
 
// MouseOverListCtrl_Sonder.h
 
class CMouseOverListCtrl_Sonder : public CListCtrl
{
	DECLARE_DYNAMIC(CMouseOverListCtrl_Sonder)
 
public:
	CMouseOverListCtrl_Sonder();
	virtual ~CMouseOverListCtrl_Sonder();
 
protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	 
	
	int m_iColumn;
	int m_iRow;
};
 
 
// MouseOverListCtrl_Sonder.cpp : Implementierungsdatei
//
 
#include "stdafx.h"
#include "Stockbruegger.h"
#include "MouseOverListCtrl_Sonder.h"
#include ".\mouseoverlistctrl_sonder.h"
 
 
// CMouseOverListCtrl_Sonder
 
IMPLEMENT_DYNAMIC(CMouseOverListCtrl_Sonder, CListCtrl)
CMouseOverListCtrl_Sonder::CMouseOverListCtrl_Sonder()
: m_bMouseOverText(false)
, m_iColumn(-1)
, m_iRow(-1)
{
}
 
CMouseOverListCtrl_Sonder::~CMouseOverListCtrl_Sonder()
{
}
 
 
BEGIN_MESSAGE_MAP(CMouseOverListCtrl_Sonder, CListCtrl)
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
 
 
 
// CMouseOverListCtrl_Sonder-Meldungshandler
 
 
//########################################################################################
//########################################################################################
void CMouseOverListCtrl_Sonder::OnMouseMove(UINT nFlags, CPoint point)
{
	//------------------ Row and Column CListCtrl ------------------------
	static LVHITTESTINFO info;
	info.pt = point;
	info.flags = LVHT_ONITEM||LVHT_ONITEMLABEL;
	//info.flags= LVHT_ONITEMLABEL;
	if ( SubItemHitTest ( &info ) == -1 )
	{
		// Error occured whilst obtaining the sub item
		return;
	}
	int nItem = info.iItem;         //row
	int nSubItem = info.iSubItem;   //column
    //----------------------------------------------------------------------
	 
 
    //---------------------------- New Cursor ----------------------------
	static CWinApp* pApp = AfxGetApp();
	static HCURSOR my_new_cursor;
	static HCURSOR hOldCursor = GetCursor();	// Store the current cursor for later use
	
	CString str=GetItemText(nItem,nSubItem);
 
 	if (nSubItem ==4 && str!="")//-------------------------------Phone
	{
       	my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); 
		 
		m_iColumn=info.iSubItem;
	    m_iRow   =info.iItem;
 
		 
	    CRect rect;
        if ( FALSE != GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect ) )
       {
            InvalidateRect( &rect );
       }
 
		
		
	}
    else if (nSubItem ==6 && str!="")//---------------------------Cell
	{
       	my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); 
	}
	else if (nSubItem ==7 && str!="")//----------------------------Alternativ Telefon
	{
       	my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); 
	}
	else if (nSubItem ==9 && str!="")//----------------------------Email
	{
       	my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); 
	}
	else
	{
		// Set the default arrow cursor
		my_new_cursor = hOldCursor;
		 
		  
		
	}
 
 	::SetCursor ( my_new_cursor );
	//-----------------------------------------------------------------------
 
 
 
 
	CListCtrl::OnMouseMove(nFlags, point);
}
//########################################################################################
//########################################################################################

Open in new window

0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22756813
Hm - I'm not sure, but it seems you have two instantiated list controls, one is 'm_List', one 'm_List_Sonder'. What is 'm_List'?

For get this working you need to have one CListCtrl-derived instance (in your case one CMouseOverListCtrl_Sonder) which subclasses the list control. This is usually done automatically with 'DDX_Control' in 'DoDataExchange'. Do you have this?
0
 

Author Comment

by:tsp2002
ID: 22756952
Yes...you are right that was my mistake.
I have to use m_List  and not m_List_Sonder
Now I get the values.
But one (simple?) question.
I would like to change the color to red when the mouse is over the cell.
Please take a look at my code. The color changes to red....but stays on red.
Will not change back to blue.

void CKontaktpersonen::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult)
 {
	 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
 
    // Take the default processing unless we set this to something else below.
    *pResult = 0;
 
    // First thing - check the draw stage. If it's the control's prepaint
    // stage, then tell Windows we want messages for every item.
 
    if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
        {
        *pResult = CDRF_NOTIFYITEMDRAW;
        }
    else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
        {
        // This is the notification message for an item.  We'll request
        // notifications before each subitem's prepaint stage.
 
        *pResult = CDRF_NOTIFYSUBITEMDRAW;
        }
    else if ( (CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage )
        {
        // 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.
        
    
            COLORREF crText, crBkgnd;
 
		
 
			//-------------- Rows in color ----------------------
		 
 
			int iCol = pLVCD->iSubItem;
			int iRow = pLVCD->nmcd.dwItemSpec;
 
	    	CString str=m_List.GetItemText(iRow,1);
 
	    	
 
 
		
			if(iCol==m_List.m_iColumn && iRow==m_List.m_iRow && m_List.m_bMouseOverText==true)
			{
				crText = RGB(255,0,0);      //rot
		        crBkgnd= RGB(240,255,255);
 
            }
			else
			{
				 crText = RGB(0,0,255);      //blau  
		         crBkgnd= RGB(240,255,255);
			}
 
/*
			ilse if(iCol==1 && str=="w")
			{
				crText = RGB(255,0,0);      //rot
		        crBkgnd= RGB(240,255,255);
 
            }
		 	else 
			{
				 crText = RGB(0,0,255);      //blau  
		         crBkgnd= RGB(240,255,255);
			}
		 
			*/
           //----------------------------------------------------
			
	  
	    // Store the colors back in the NMLVCUSTOMDRAW struct.
        pLVCD->clrText = crText;
        pLVCD->clrTextBk = crBkgnd;
 
        // Tell Windows to paint the control itself.
        *pResult = CDRF_DODEFAULT;
        }
		 
}
  
 //###########################################################################################

Open in new window

0
 
LVL 31

Accepted Solution

by:
Zoppo earned 500 total points
ID: 22757089
Hi,

yes, that's right - you need to invalidate both items, the new selected one and the previous selected one ... you can do it in OnMouseMove i.e. like this:

CRect rect;
if ( m_iColumn != info.iSubItem || m_iRow != info.iItem )
{
 // you only need to invalidate anything when the cell beyond the mouse has changed
 if ( false != m_bMouseOverText )
 {
  // invalidate previously selected cell
  if ( FALSE != GetSubItemRect( m_iRow, m_iColumn, LVIR_BOUNDS, rect ) )
  {
   InvalidateRect( &rect );
  }

  if (nSubItem ==4 && str!="")//-------------------------------Phone
  {
   my_new_cursor = pApp->LoadCursor(IDC_CURSOR2);

   m_iColumn=info.iSubItem;
   m_iRow   =info.iItem;

   UpdateData(true);
   if ( FALSE != GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect ) )
   {
    InvalidateRect( &rect );
   }
  }
  ...
}

I hope that helps,

ZOPPO
0
 

Author Comment

by:tsp2002
ID: 22757270
S U P E R  it works....thanks a lot for your great help.
I very appreciate you help.
Have a nice day.
Best regards,
Thomas
P.S. still a beginner in MFC
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 22757299
You're welcome - I'm glad I could help you ...

Have a nice day,

best regards,

ZOPPO
0

Featured Post

Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

786 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question