Solved

Need some help with CListCtrl

Posted on 2008-10-15
23
681 Views
Last Modified: 2013-11-20
I would like to find out the row and column when the mouse is over my CListCtrl.
       int nRow = hti.iItem;
       int nCol = hti.iSubItem;

I allways get nRow as -1
The nCol works fine....see below....when the mouse is over column 5 the cursor changes.
How can I get the nRow??????????
Please help.
I did ask the question before....Need some help with CListView and SetCursor....but until now no solution.
500 poinst Grade A when it works.
Best regards,
Thomas



BOOL CKontaktpersonen::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)

 {

	 BOOL bReturn;

	 CRect rcBtn;

	 CPoint ptCursor;
 

	 CWnd* pBtn = GetDlgItem(IDC_LIST1);
 

	 pBtn->GetWindowRect(rcBtn);

	 GetCursorPos(&ptCursor);
 

     LVHITTESTINFO hti;

	 hti.pt=ptCursor;

	 m_List.SubItemHitTest( &hti );

    

       // you are over an item

       int nRow = hti.iItem;

       int nCol = hti.iSubItem;
 

	   if(rcBtn.PtInRect(ptCursor)==TRUE && hti.iSubItem==5)

	{

		CWinApp* pApp = AfxGetApp();

		HCURSOR my_new_cursor = pApp->LoadCursor(IDC_CURSOR2); // load new cursor

		SetCursor(my_new_cursor);

		bReturn = TRUE;

	}

	   else

	   {

           	bReturn = CDialog::OnSetCursor(pWnd, nHitTest, message);// default cursor

	   }

	

	return bReturn;
 

	 

	 return CDialog::OnSetCursor(pWnd, nHitTest, message);

 }

Open in new window

0
Comment
Question by:tsp2002
  • 9
  • 7
  • 6
  • +1
23 Comments
 
LVL 19

Accepted Solution

by:
mrwad99 earned 400 total points
Comment Utility
Create your own CListCtrl derived class.  Override OnMouseMove and call SubItemHitTest, as the code below shows.  Then you can add your code to change the cursor using the values of nItem and nSubItem.

HTH :)
// .h

class CMyListCtrl : public CListCtrl

{

	DECLARE_MESSAGE_MAP()

public:

	afx_msg void OnMouseMove( UINT nFlags, CPoint point );

};
 

//.cpp
 
 

BEGIN_MESSAGE_MAP(CMyListCtrl,CListCtrl)

	ON_WM_MOUSEMOVE()

END_MESSAGE_MAP()
 

/*afx_msg*/ void CMyListCtrl::OnMouseMove( UINT nFlags, CPoint point )

{

	static LVHITTESTINFO info;

	info.pt = point;

	info.flags = LVHT_ONITEM;

	if ( SubItemHitTest ( &info ) == -1 )

	{

		// Error occured whilst obtaining the sub item

		return;

	}

	int nItem = info.iItem;

	int nSubItem = info.iSubItem;

	TRACE ( _T("Over item %i, subitem %i\n"), nItem, nSubItem );

}

Open in new window

0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 100 total points
Comment Utility
First, you should make some checks in CKontaktpersonen::OnSetCursor, i. e. if the mouse is over the list control:

  CWnd* pList = GetDlgItem(IDC_LIST1);
  if (pList != pWnd || nHitTest != HTCLIENT)  // list control client area
     return FALSE;  // returning false means that the default handling of the
                            // parent class was invoked

>>>> CWnd* pBtn
Why do you call a list control pBtn????

>>>> hti.pt=ptCursor;

The ptCursor is in Windows coordinates while the hti expects the point in client coordinates. Client coordinates have the top-left corner = (0, 0).

You can use the ScreenToClient member function to convert (before assigning)

   pList->ScreenToClient(ptCursor);

>>>> LVHITTESTINFO hti;
You better initialize structures before use:

    LVHITTESTINFO hti = { 0 };  // makes all zero

>>>> m_List.SubItemHitTest( &hti );
Two issues here. First, if you have an m_List you can spare the GetDlgItem(IDC_LIST).

   if (&m_List != pWnd || nHitTest != HTCLIENT)  // list control client area

Second, always check for the return of a function, normally  to detect possible errors. Here, the return code isn't an error but the missing item id (row)

  int nRow = m_List.SubItemHitTest( &hti );
  if (nRow < 0) return FALSE;  // not above an item
  int  nCol = hto.isSubItem;

Unfortunately the SubItemHitTest doesn't set the iItem member (what is a bug in my opinion) and last year I was trapped by that bug same as you (I added much code to compute the row from the control's sizes).








0
 
LVL 19

Expert Comment

by:mrwad99
Comment Utility
>> Unfortunately the SubItemHitTest doesn't set the iItem member

Yeah, I have found that when playing with the questioner's code, but it seems to work when calling it from within the overridden OnMouseMove, as I posted...
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> when calling it from within the overridden OnMouseMove

The OnMouseMove has the disadvantage that you need to capture the mouse to get it called while the OnSetCursor was called without capturing.

>>>> Unfortunately the SubItemHitTest doesn't set the iItem member
As the SubItemHitTest returns the row number, it is not really a problem (if you know).

>>>> int nItem = info.iItem;

Interesting that it works in your code. I would guess it is because

     info.flags = LVHT_ONITEM;

???
0
 
LVL 3

Expert Comment

by:Naveen_R
Comment Utility
When you call GetCursorPosition(), it return the position relative to the screen. You need to convert it relative to the top left of List control. you can use the ScreenToClient() function for this.
LVHITTESTINFO hti;

hti.pt = ptCursor;

m_List.ScreenToClient( &hti.pt );

m_List.SubItemHitTest( &hti );

Open in new window

0
 

Author Comment

by:tsp2002
Comment Utility
What did i miss. Please look at my code. Will not work.
Will not stop at MouseMove even when I put a stop button here:static LVHITTESTINFO info;
So I guess I did something wrong.
Please help, thanks
// MyListCtrl.cpp : Implementierungsdatei

//
 

#include "stdafx.h"

#include "Stockbruegger.h"

#include "MyListCtrl.h"

#include ".\mylistctrl.h"
 
 

// MyListCtrl
 

IMPLEMENT_DYNAMIC(MyListCtrl, CListCtrl)

MyListCtrl::MyListCtrl()

{

}
 

MyListCtrl::~MyListCtrl()

{

}
 
 

BEGIN_MESSAGE_MAP(MyListCtrl, CListCtrl)

	ON_WM_MOUSEMOVE()

END_MESSAGE_MAP()
 
 
 

// MyListCtrl-Meldungshandler
 
 

void MyListCtrl::OnMouseMove(UINT nFlags, CPoint point)

{

	static LVHITTESTINFO info;

	info.pt = point;

	info.flags = LVHT_ONITEM;

	if ( SubItemHitTest ( &info ) == -1 )

	{

		// Error occured whilst obtaining the sub item

		return;

	}

	int nItem = info.iItem;

	int nSubItem = info.iSubItem;

	TRACE ( _T("Over item %i, subitem %i\n"), nItem, nSubItem );
 
 
 

	CListCtrl::OnMouseMove(nFlags, point);

}

#pragma once
 
 

// MyListCtrl
 

class MyListCtrl : public CListCtrl

{

	DECLARE_DYNAMIC(MyListCtrl)
 

public:

	MyListCtrl();

	virtual ~MyListCtrl();
 

protected:

	DECLARE_MESSAGE_MAP()

public:

	afx_msg void OnMouseMove(UINT nFlags, CPoint point);

};

// Kontaktpersonen.cpp : Implementierungsdatei

//
 

#include "stdafx.h"

#include "Stockbruegger.h"

#include "Kontaktpersonen.h"

#include ".\kontaktpersonen.h"

#include "tapi.h"

#include "MsgBox.h"

#include "MyListCtrl.h"

Open in new window

0
 

Author Comment

by:tsp2002
Comment Utility
#include "MyListCtrl.h"
to add only the header to my program?
I think I have to do more to override the mousemove?????
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 100 total points
Comment Utility
>>>> So I guess I did something wrong.
You need to subclass the MyListCtrl for your list control so that the messages were passed to your handler. You do that by changing the m_List member in your dialog from CListCtrl to CMyListCtrl in the dialog class header. Then the DoDataExchange of your diaöog class will do the subclassing (means to change message targeting from CListCtrl to CMyListCtrl) when it was called from the framework for initialization.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> when I put a stop button here:static LVHITTESTINFO info;
Set your breakpoints to other statements and *not* to a static definition. These statements were executed only once for initialization.
0
 
LVL 19

Expert Comment

by:mrwad99
Comment Utility
>> Interesting that it works in your code. I would guess it is because

Unfortunately not.  I added that myself and still get iItem as -1.

>> You need to subclass the MyListCtrl

Alex is right; and I guess I should have said that in my original comment.  In my dialog class (CMyDlg) that hosts the control, I have the following (IDC_LIST3 is the ID of the list control)



// .h
 

class CMyDlg : public CDialog

{

   // ...Other bits and bobs

protected:

    CMyListCtrl m_wndList;
 

};
 

// .cpp
 

void CMyDlg::DoDataExchange(CDataExchange* pDX)

{

    CDialog::DoDataExchange(pDX);

    // ... Other DDX members...

    DDX_Control(pDX, IDC_LIST3, m_wndList);

}

Open in new window

0
 

Author Comment

by:tsp2002
Comment Utility
thank you it works .... I get the row and col .
But how and where can I now change the cursor.
I want like to change the cursor when I am over column 5, and in the cell (row) is a e-mail address, also I want to change the color of the e-mail address.
Please help, thanks so much
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:tsp2002
Comment Utility
Should I use OnSetCursor in the  CKontaktpersonen, I tried to add a OnSetCursor Function to the new Class CMyCListCtrl....but that will not work
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> Should I use OnSetCursor in the  CKontaktpersonen
You could use the CMyListCtrl::OnMouseMove or CKontaktpersonen::OnSetCursor but not both. The first inly works for the control when the mouse moves were above while the second works for your dialog (and all controls of the dialog).
0
 
LVL 19

Assisted Solution

by:mrwad99
mrwad99 earned 400 total points
Comment Utility
You already have the code to change the cursor, you just need to put it in the right place.  Your original code is trying to change the cursor when the mouse is moved over the fifth subitem in your control:

    if(rcBtn.PtInRect(ptCursor)==TRUE && hti.iSubItem==5)
    {
        // Change cursor
    }


We don't need the check to see if the cursor is over the control, since if the cursor was not over the control, OnMouseMove would not have been called.  In my code, I added a trace statement to show the current item/subitem:

    int nItem = info.iItem;
    int nSubItem = info.iSubItem;
    TRACE ( _T("Over item %i, subitem %i\n"), nItem, nSubItem );


You have all the information you need in the variables nItem and nSubItem.
void MyListCtrl::OnMouseMove(UINT nFlags, CPoint point)

{

	static LVHITTESTINFO info;

	info.pt = point;

	info.flags = LVHT_ONITEM;

	if ( SubItemHitTest ( &info ) == -1 )

	{

		// Error occured whilst obtaining the sub item

		return;

	}

	int nItem = info.iItem;

	int nSubItem = info.iSubItem;

	TRACE ( _T("Over item %i, subitem %i\n"), nItem, nSubItem );
 

 	static CWinApp* pApp = AfxGetApp();

	static HCURSOR my_new_cursor;

	static HCURSOR hOldCursor = GetCursor();	// Store the current cursor for later use

	

 	if ( nSubItem ==5)

	{

		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 19

Expert Comment

by:mrwad99
Comment Utility
PS tsp2002, when closing this question, remember several experts were involved in helping you :)
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> I want like to change the cursor when I am over column 5
When changing the cursor you should capture the mouse, cause you need to make it reverse when the mouse is outside of the control's rectangle.

Both YourDialog::OnSetCursor and MyListCtrl::OnMouseMove only were called as long as the mouse is above the dialog respectively the control. When capturing the mouse the OnMouseMove handler was called even if the mouse leaves the control's region so that you can restore the original cursor shape.

You capture the mouse calling SetCapture :

   // newly capture mouse
   CWnd* pWnd = GetCapture();
   if (pWnd == this)
   {
         // you already have captured the mouse
         // check whether the point is still within your list control
         // if not release capturing calling ReleaseCapture() and return.
         
   }
   SetCapture();
   {
   }
        // get row and column
        ...
       
  }

Note, after capturing you need
0
 
LVL 19

Expert Comment

by:mrwad99
Comment Utility
>> ...cause you need to make it reverse when the mouse is outside of the control's rectangle.

The default implementation of <ParentWindow>::OnSetCursor() will do that, unless I have missed something??
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> The default implementation of <ParentWindow>::OnSetCursor() will do that, unless I have missed something??

As far as I understand mouse capturing, other windows won't get any mouse events as long as the mouse was captured. That includes the dialog window if the mouse capturing was done for the list control (window).
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
You change the cursor by calling SetCursor(hCursor);

The hCursor is a handle to a new cursor which can be retrieved e. g. by LoadCursorFromFile.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> The default implementation of <ParentWindow>::OnSetCursor() will do that,

Ok, I understand. You were suggesting to not capturing the mouse but simply change the cursor's shape while the MyListControl::OnMouseMove was called. If the mouse was outside of the control Dialog::OnSetCursor()  will restore the arror shape.
0
 

Author Closing Comment

by:tsp2002
Comment Utility
Thank you so much for your great help. It works just fine for me now.
From here I hope I can work alone...otherwise I will ask a new question.
The SubItemHitTest is difficult for a beginner. I have split the points. I would give you more....but 500 are the most you can give.
Have a nice day and thanks again.
Best regards,
Thomas
0
 
LVL 19

Expert Comment

by:mrwad99
Comment Utility
>> Ok, I understand...

Yeah, that is what I was getting at Alex.

Thomas: glad we could be of help; all the best :)
0
 

Author Comment

by:tsp2002
Comment Utility
mrwad99:it was a pleasure, Thank You
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

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
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 viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

763 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

10 Experts available now in Live!

Get 1:1 Help Now