Discussion:
CListCtrl::GetTopIndex() returns negative value?
(too old to reply)
nomad
2006-10-04 12:42:01 UTC
Permalink
According to the documentation, GetTopIndex() should return the index of the
topmost visible item in a list control when the list control is in list view
or report mode.

I have a virtual list control (i.e., the data are supplied by my app). I
have to occasionally rebuild the list (for reasons that are not germane), and
before I rebuild the contents of the list, I get the topmost current item, so
that I can restore it to the top after I'm done (using EnsureVisible()).

I've noticed that occasionally I get a negative value out of GetTopIndex().
Why should that happen, and what does it mean?
David Lowndes
2006-10-04 16:13:42 UTC
Permalink
Post by nomad
I've noticed that occasionally I get a negative value out of GetTopIndex().
Do you have any idea what this "occasionally" situation is?

Since the documentation doesn't mention that LVM_GETTOPINDEX can
return a negative value, I don't know why it could arise unless you
have a derived class that's handling the message incorrectly?

Dave
nomad
2006-10-04 16:26:02 UTC
Permalink
Post by David Lowndes
Do you have any idea what this "occasionally" situation is?
Sure. My derived class populates the list control using code similar to the
following:

localLock = ctrl->LockWindowUpdate();

topItem = ctrl->GetTopIndex();

/*
Clear the list view control and repopulate it to reflect the
current data state.
*/
... rebuild the data ...

VERIFY(ctrl->SetItemCountEx(numDataItems));

/*
Restore the topmost item to the list.
*/
(void) ctrl->EnsureVisible(ctrl->GetItemCount() - 1, False);
(void) ctrl->EnsureVisible(topItem, False);
ctrl->Invalidate();

if (localLock)
{
ctrl->UnlockWindowUpdate();
}

If I have a large number of items, such that the number of items extends
below the edge of the list control window, and I scroll down to the bottom of
the list, so that the upper items scroll up beyond the top of the list
control window, and then force the above rebuild to occur, I get negative
values back from GetTopIndex().
Post by David Lowndes
Since the documentation doesn't mention that LVM_GETTOPINDEX can
return a negative value, I don't know why it could arise unless you
have a derived class that's handling the message incorrectly?
I have a derived class (actually, a derived class and another derived from
that), but neither handles the LVM_GETTOPINDEX message directly; all that is
left to the base method or the list control itself.
David Lowndes
2006-10-04 19:57:50 UTC
Permalink
Post by nomad
Sure. My derived class populates the list control using code similar to the
localLock = ctrl->LockWindowUpdate();
OK, don't use LockWindowUpdate for a start. I don't know if that's a
cause of the problem, but it's a specialised function not intended for
general use. I believe it has a specific use internal to Windows for
drawing the old style dotted rectangle. The recommended method is to
use SetRedraw.
Post by nomad
I have a derived class (actually, a derived class and another derived from
that), but neither handles the LVM_GETTOPINDEX message directly; all that is
left to the base method or the list control itself.
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?

Dave
nomad
2006-10-05 08:46:02 UTC
Permalink
OK, don't use LockWindowUpdate for a start. ... The recommended method is to
use SetRedraw.
Okay, well, I tried that, but SetRedraw() isn't as reliable as
LockWindowUpdate(). My control doesn't get updated properly when I use it.
I'm probably not being as thorough as I should be, and I'll take a look at
using SetRedraw() some more later.

In any event, that makes no difference. I replaced the calls to
LockWindowUpdate() with SetRedraw() and the same thing happens: SetTopIndex()
returns a negative value.
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?
Nope. I explicitly declare each message separately. Between the two of them,
here are the messages I handle:

ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)

ON_NOTIFY_REFLECT_EX(LVN_GETDISPINFO, OnGetDispInfo)
ON_NOTIFY_REFLECT_EX(LVN_HOTTRACK, OnHotTrack)
ON_NOTIFY_REFLECT_EX(LVN_ITEMCHANGED, OnListChange)
ON_NOTIFY_REFLECT_EX(LVN_ODCACHEHINT, OnCacheHint)
ON_NOTIFY_REFLECT_EX(LVN_ODSTATECHANGED, OnListChange)
ON_NOTIFY_REFLECT_EX(LVN_SETDISPINFO, OnSetDispInfo)

ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndLabelEdit)
ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW, OnCustomDraw)
David Lowndes
2006-10-05 11:13:46 UTC
Permalink
Post by nomad
Okay, well, I tried that, but SetRedraw() isn't as reliable as
LockWindowUpdate(). My control doesn't get updated properly when I use it.
Try either not using, or moving your call to Invalidate till after
you've re-enabled drawing. SetRedraw is the one to use.
Post by nomad
In any event, that makes no difference. I replaced the calls to
LockWindowUpdate() with SetRedraw() and the same thing happens: SetTopIndex()
returns a negative value.
When is the code you showed being called?
Post by nomad
Post by David Lowndes
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?
Nope. I explicitly declare each message separately. Between the two of them,
...
Are any of those message handlers invoked when your code calls
GetTopIndex?

Dave
nomad
2006-10-09 08:49:01 UTC
Permalink
Haven't had a chance to play with your suggestions; had to ship on Friday.
When I get some time, I'll let you know what I find out.

Thanks.
nomad
2006-10-13 10:26:01 UTC
Permalink
Post by David Lowndes
Try either not using, or moving your call to Invalidate till after
you've re-enabled drawing. SetRedraw is the one to use.
I've replaced my calls to LockWindowUpdate() with SetRedraw() now, and,
after a bit of fiddling, things work fine in that respect. No more desktop
flicker, which is a bonus.
Post by David Lowndes
Post by David Lowndes
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?
Are any of those message handlers invoked when your code calls
GetTopIndex?
Checked this just now with some debugging statements, and, no, nothing gets
called during the call to GettopIndex() and, yes, it still returns a negative
value, even with the SetRedraw() stuff in place.
David Lowndes
2006-10-13 10:47:13 UTC
Permalink
Post by nomad
Post by David Lowndes
Try either not using, or moving your call to Invalidate till after
you've re-enabled drawing. SetRedraw is the one to use.
I've replaced my calls to LockWindowUpdate() with SetRedraw() now, and,
after a bit of fiddling, things work fine in that respect. No more desktop
flicker, which is a bonus.
Good, one minor improvement then. :)
Post by nomad
Post by David Lowndes
Post by David Lowndes
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?
Are any of those message handlers invoked when your code calls
GetTopIndex?
Checked this just now with some debugging statements, and, no, nothing gets
called during the call to GettopIndex() and, yes, it still returns a negative
value, even with the SetRedraw() stuff in place.
All I can suggest at this stage is to contact MS support (by
telephone) and see what they have to say. If you can generate a simple
program that illustrates it happening it will probably make things
easier to progress. At the very least there's a documentation omission
that probably needs to be cleared up.

If you do get an answer, let us know!

Dave
nomad
2006-10-13 10:26:02 UTC
Permalink
Post by David Lowndes
Try either not using, or moving your call to Invalidate till after
you've re-enabled drawing. SetRedraw is the one to use.
I've replaced my calls to LockWindowUpdate() with SetRedraw() now, and,
after a bit of fiddling, things work fine in that respect. No more desktop
flicker, which is a bonus.
Post by David Lowndes
Post by David Lowndes
Any chance you handle it inadvertently by handling a range of messages
that occupy the same value?
Are any of those message handlers invoked when your code calls
GetTopIndex?
Checked this just now with some debugging statements, and, no, nothing gets
called during the call to GettopIndex() and, yes, it still returns a negative
value, even with the SetRedraw() stuff in place.
r***@gmail.com
2015-02-19 18:12:28 UTC
Permalink
I also had an issue where GetTopIndex() would return a negative value during a window resize where horizontal scroll bars would hide when the window grew large enough that they weren't needed.

In my case, I was also calling SetColumnWidth() from within the OnSize() handler. It seems as though the combination of the WM_SIZE message in conjunction with LVM_SETCOLUMWIDTH was causing the list control some issues.

Once I removed the call to SetColumnWidth() from the OnSize() handler and instead triggered it from the parent's OnExitSizeMove() handler, I no longer saw the issue with GetTopIndex() returning < 0.

A similar approach is discussed in this post which I found after the fact.

https://groups.google.com/forum/#!searchin/microsoft.public.vc.mfc/CListCtrl$20GetTopIndex$20negative/microsoft.public.vc.mfc/sUlu7EkUUfc/sk-2GhDUjccJ
Loading...