Steve Valliere
Steve Valliere

Reputation: 1207

ListView_GetNextItem(LVNI_VISIBLEONLY) not working

I need to process all of the items in a ListView (in report mode) that are visible to the user. While debugging, I tried this:

int  item;
HWND hlist;

for( item = ListView_GetNextItem( hlist, -1, LVNI_VISIBLEONLY );
     item >= 0;
     item = ListView_GetNextItem( hlist, item, LVNI_VISIBLEONLY ) )
{
   if( ListView_IsItemVisible( hlist, item ) )
   {
      OutputDebugString("DEBUG:  Found a visible item!\n");
   }
}

According to the documentation, the GetNextItem loop should provide all of the items that are VISIBLE (in Vista or later and I'm on Win7). However, my debug shows that it actually provides ALL items in the ListView.

So I added the redundant test of IsItemVisible, which is documented to return TRUE when the specified item is VISIBLE. Unfortunately, it returns FALSE for every single item found in the for() loop.

Obviously I'm missing something critical (and possibly obvious) here, but I'm at a total loss as to what it could be. Oh, FWIW I'm testing under Windows 7 Professional x64, and my build target is WINVER 0x0601 (_WIN32_WINNT is also 0x0601).

Any ideas what I'm missing?

[update]

I've found a work-around (see below) but I'd still love to know why neither of the visible-related functions is working (for me).

For anyone interested, here's how I'm working around the problem:

   LVITEM         lvi;
   HWND           hlist = GetDlgItem( hwnd, LST_ALARMS );
   int            item;
   RECT           rcList;
   RECT           rcItem;
   RECT           rcHead;

   GetClientRect( hlist, &rcList );
   // Remove the header space from the visible item area
   GetClientRect( ListView_GetHeader(hlist), &rcHead );
   rcList.top += (rcHead.bottom - rcHead.top);

   memset( &lvi, 0, sizeof(lvi) );

   for( item = ListView_GetNextItem( hlist, -1, LVNI_ALL );
        item >= 0;
        item = ListView_GetNextItem( hlist, item, LVNI_ALL ) )
   {
      if( ListView_GetItemRect( hlist, item, &rcItem, LVIR_BOUNDS )
         && (rcList.top <= rcItem.top)
         && (rcItem.bottom <= rcList.bottom) )
      {  // This item is COMPLETELY visible 
         // -- partially visible items are NOT included.
         lvi.mask  = LVIF_PARAM;
         lvi.iItem = item;
         ListView_GetItem( hlist, &lvi );
         // do stuff with item
      }
   }

This is working exactly as I hoped the things at the beginning of this message would do. I'd prefer the easier readability of the visibility functions, but since I need it to actually work, I'm stuck comparing rectangles.

Upvotes: 2

Views: 510

Answers (2)

Raymond Chen
Raymond Chen

Reputation: 45172

The concept of visibility used by LVNI_VISIBLEONLY is not "visible on screen right now". It is "has not been removed from view". (An object becomes removed from view if it belongs to a group that has been collapsed.) So it is expected that LVNI_VISIBLEONLY returns all elements if you are not using collapsible grouping.

In other words, "visible" means "not hidden", not "on screen". This is the same sense used by IsWindowVisible and TVNI_NEXTVISIBLE and other APIs.

Upvotes: 3

rrirower
rrirower

Reputation: 4590

There's a curious note at the bottom of the LVM_GETNEXTITEM explanation that may explain your problem:

Remarks

Note that the following flags, for use only with Windows Vista, are mutually exclusive of any other flags in use: LVNI_VISIBLEONLY, LVNI_SAMEGROUPONLY, LVNI_VISIBLEORDER, LVNI_DIRECTIONMASK, and LVNI_STATEMASK.

Upvotes: 0

Related Questions