Solved

Need some help with CListCtrl and color change

Posted on 2008-10-20
19
1,144 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 30

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
 
LVL 30

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 30

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 30

Expert Comment

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

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 

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 30

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 30

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 30

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 30

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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

708 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

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now