• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1249
  • Last Modified:

Need some help with CListCtrl and color change

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
tsp2002
Asked:
tsp2002
  • 11
  • 8
1 Solution
 
ZoppoCommented:
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
 
tsp2002Author Commented:
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
 
tsp2002Author Commented:
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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
ZoppoCommented:
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
 
tsp2002Author Commented:

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
 
ZoppoCommented:
Ah, sorry - a mistype - the rect is passed per reference, not as pointer, so it's:

> GetSubItemRect( nItem, nSubItem, LVIR_BOUNDS, rect )

ZOPPO
0
 
tsp2002Author Commented:
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
 
tsp2002Author Commented:
sorry...did the breakpoint wrong.
but will redraw hole ListView

aaa.BMP
0
 
ZoppoCommented:
Please check the rect when you place a breakpoint in the line where InvalidateRect is called.
0
 
tsp2002Author Commented:
sorry I did a mistake....will try something...I think it works
0
 
tsp2002Author Commented:
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
 
tsp2002Author Commented:
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
 
ZoppoCommented:
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
 
tsp2002Author Commented:

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
 
ZoppoCommented:
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
 
tsp2002Author Commented:
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
 
ZoppoCommented:
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
 
tsp2002Author Commented:
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
 
ZoppoCommented:
You're welcome - I'm glad I could help you ...

Have a nice day,

best regards,

ZOPPO
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 11
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now