Solved

Need some help with CListCtrl

Posted on 2008-10-15
23
704 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 7
  • 6
  • +1
23 Comments
 
LVL 19

Accepted Solution

by:
mrwad99 earned 400 total points
ID: 22722137
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
ID: 22722567
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
ID: 22722596
>> 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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22722779
>>>> 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
ID: 22727999
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
ID: 22728720
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
ID: 22728863
#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
ID: 22728963
>>>> 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
ID: 22728974
>>>> 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
ID: 22728999
>> 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
ID: 22729199
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
 

Author Comment

by:tsp2002
ID: 22729210
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
ID: 22729271
>>>> 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
ID: 22729284
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
ID: 22729290
PS tsp2002, when closing this question, remember several experts were involved in helping you :)
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22729348
>>>> 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
ID: 22729362
>> ...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
ID: 22729485
>>>> 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
ID: 22729592
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
ID: 22729626
>>>> 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
ID: 31506285
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
ID: 22729758
>> 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
ID: 22729959
mrwad99:it was a pleasure, Thank You
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

717 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