Programmatically selecting (giving focus to) an item within a CListCtrl after redrawing it.

Hi everyone,

I am currently working in the Visual Studio 2008 IDE, under Windows XP. I am using the Unicode character set.

I am writing an application with a CListCtrl. I am constantly updating the CListCtrl with new information by clearing its contents and then re-adding the updated information to it.

The problem I am having is that when I "Refresh" (update the information) the CListCtrl it will lose the position of the selected item, so to the user it would appear to jump to the top of the list. This is an obvious consequence of redrawing the CListCtrl.

To remedy this I have tried to implement a routine that stores the index of the selected item before the CListCtrl is cleared using (which functions correctly),

int nSelectedItemIndex = CListCtrl::GetNextItem(-1, LVNI_FOCUSED)
after the list has been reconstituted I then attempt to set the state of the item at the index given by the above method with,

1
2
3
4
5
6
7
8
9
10
11
12
13
LVITEM lvSelector;
ZeroMemory( &lvSelector, sizeof(LVITEM) );

lvSelector.mask		= LVIF_STATE;

lvSelector.iItem	= nSelectedItemIndex;
lvSelector.iSubItem	= 0;
lvSelector.state	= LVIS_FOCUSED;
	
if( m_ListCtrlTest.SetItemState(nSelectedItemIndex, &lvSelector) == 0)
{
	MessageBox(TEXT("Selection Failed"), TEXT("Selector"), MB_OK);
}


however, although the CListCtrl::SetItemState(...) method returns success item at index nSelectedItemIndex is NOT selected (surrounded by the blue rectangle).

Have I misunderstood the documentation of this method? Can anyone suggest an alternative solution to my problem?

Thanks in advance for any help you can offer.
I am writing an application with a CListCtrl. I am constantly updating the CListCtrl with new information by clearing its contents and then re-adding the updated information to it.

The problem I am having is that when I "Refresh" (update the information) the CListCtrl it will lose the position of the selected item, so to the user it would appear to jump to the top of the list. This is an obvious consequence of redrawing the CListCtrl.
No, it's a consequence of clearing its contents and then re-adding the updated information.

User GetTopIndex to find what's the top item. As far as I can see, the best you can do is call EnsureVisible to ensure that item is selected.

A better way of course, is to update the item and call Update on that item to refresh it.
@kbw: Thanks for your reply!

No, it's a consequence of clearing its contents and then re-adding the updated information.

That was a semantic error on my part, I meant to say clearing and re-adding :)

A better way of course, is to update the item and call Update on that item to refresh it.

The problem with using Update is that I don't think the data in the CListCtrl will change, (please feel free to correct me if I'm wrong). I need to Update the values in the List View items.

I tried using GetTopIndex(...) and GetCountPerPage(...) in tandem to get the last visible item but (I believe) because of the presence of Group Headers, GetCountPerPage(...) returns an incorrect value.

I solved the problem (somewhat), by using CListCtrl::GetClientRect(...) and CListCtrl::GetItemRect(...).

I iterated through all the CListCtrl items, extracting the "RECT::bottom" values until I found a value that was greater than the "RECT::bottom" of the CListCtrl. I then had the index of the first "Off Screen" CListCtrl item, and by deducting 1 from that index I got the Last Visible Item.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Gets the index of the LAST visible item in the CListCtrl

RECT ItemRect;	//stores the RECT values of a ListView Item within the CListCtrl
RECT ListViewRect;  //stores the RECT values of the CList Ctrl itself
int nLastVisibleItem;  //stores the index of the Last Visible Item

m_ListCtrlTest.GetClientRect(&ListViewRect);	//gets the CListCtrl's RECT values

int nLastItem = m_ListCtrlTest.GetItemCount();	//gets the number of ListView items in the CListCtrl

for (int i = 0; i < nLastItem; i++)
{
	//gets the RECT values of the current ListView item
	m_ListCtrlTest.GetItemRect(i, &ItemRect, LVIR_BOUNDS);

	//if item rect is outside the bounds of the CListCtrl rect then we have
	//found the index of the first item that "off screen"
	if (ListViewRect.bottom < ItemRect.bottom)
	{
		//-1 to get the index LAST VISIBLE ITEM
		nLastVisibleItem = n - 1;
		break;
	}
}


I then use the CListCtrl::EnsureVisible(LastVisibleItem, FALSE) to make it scroll to the appropriate location.

Once again, thank you for all your help!
The problem with using Update is that I don't think the data in the CListCtrl will change
You can batch the updates with SetRedraw(FALSE) then SetRedraw(TRUE). The parameter is defaulted to TRUE.

You didn't mention groups. Anyway, try the SetRedraw clause around your updates and see what it's like.
@kbw, I'll give it a go. Thanks for the information!
Topic archived. No new replies allowed.