(Virtual) ListView items get wrong content when hovering over them, from bottom to top

喜你入骨 提交于 2019-12-11 00:11:30

问题


I use C++ Builder 2009 and have implemented a Listview virtually.

Recently I learned of an issue that (so far) only happens for 3 people out of thousands. (Obviously there must be more instances, but they remain unreported so far).

What happens is that when the user hovers over the items in the ListView control, from bottom to top, the items in the list change. Since the control is implemented virtually the software provides the information via an event and for 'a' reason the software gets the wrong index to work with when hovering from bottom to top.

See this little video to understand the behaviour better: https://www.youtube.com/watch?v=jNZkaBH24PY

I have been searching for a reason, on and off, for some time, hampered by the fact that I cannot repeat this problem myself, so I needed to send special builds to 1 of the 3 willing to help. And I was finally able to pinpoint it to the use of Overlay icons !

I assign an icon to the items in the list by asking Windows what icon is relevant via SHGetFileInfo

The event:

void __fastcall TFinder::ListViewData(TObject *Sender, TListItem *Item)
{
DListView->OnData(Item, DirInListView->Object(Item->Index)) ;
}

Code called by the event:

HANDLE ObjectDisplayInListView::OnData (TListItem *ListItem, DataObject *Object)
{

ListItem->Data      = (void*) Object ;

ListItem->Caption   = String(Object->DisplayName().c_str()) ;

if (!SystemIconsSet)
    {
    ListItem->ImageIndex    = Object->Icon() ;
    ListItem->OverlayIndex  = Object->IconOverlay() ;
    }
else
    {
    int Icon = SystemIcon(Object) ;
    ListItem->ImageIndex    = (Icon & 0x00FFFFFF) ;
    ListItem->OverlayIndex = (Icon >> 24) - 1 ;
    }

ListItem->Cut           = Object->IconGreyed() ;
ListItem->StateIndex    = Object->IconState() ;

return (HANDLE) ListItem ;
}

// The SystemIcon() routine:

int ObjectDisplayInListView::SystemIcon (DataObject *Object)
{
// SHFILEINFO info ; The class declared one will do !

info.hIcon = NULL ; // Just making sure

DWORD Res = SHGetFileInfoW( Object->DisplayName().c_str(),
                            FILE_ATTRIBUTE_NORMAL,
                            &info,
                            sizeof(SHFILEINFO) ,
                            SHGFI_ICON |
                            SHGFI_USEFILEATTRIBUTES |
                            SHGFI_OVERLAYINDEX
                                ) ;
DestroyIcon(info.hIcon) ; 

return info.iIcon ;
}

The subclass code (which can be removed completely and the problem still exists !!)

void __fastcall TFinder::LVNewWindowProc(Messages::TMessage &Msg)
{
if( LVOldWindowProc ) LVOldWindowProc( Msg );
if (Msg.Msg == WM_NOTIFY) // Sort arrows
    {
    // Not relevant for this Q but added since it is in my code
    switch(((LPNMHDR)Msg.LParam)->code)
        {
        case HDN_ENDTRACKA:
        case HDN_ENDTRACKW:
        case HDN_ITEMCHANGEDA:
        case HDN_ITEMCHANGEDW:
            {
            if(((LPNMHDR)Msg.LParam)->hwndFrom == ListView_GetHeader(ListView->Handle))
                {
                if (ObjInListView && dynamic_cast<FileDirObject*>(ObjInListView) && ListView->Items->Count)
                    {
                    SetSortArrow(SortingTypeToDesignColumn(UISortingType), UISortingDirection) ; // Show the arrow based on what sorting was used
                    }
                }
            }
        break;
        }
    }
else if (Msg.Msg == CN_NOTIFY && Platform > OS_PLATF_WINXP) 
    {                          
    if (reinterpret_cast<LPNMHDR>(Msg.LParam)->code == LVN_GETDISPINFOW)
        {
        LV_ITEM &item = reinterpret_cast<LV_DISPINFO*>(Msg.LParam)->item ; 
        int  OverlayIndex = -1 ;

        TListItem *ListItem = ListView->Items->Item[item.iItem] ;
        if (ListItem) OverlayIndex = ListItem->OverlayIndex ;

        if (OverlayIndex >= 0)
            {
            item.mask |= LVIF_STATE ;
            item.state |= INDEXTOOVERLAYMASK(OverlayIndex + 1) ;
            item.stateMask |= LVIS_OVERLAYMASK ;
            }
        }
    }
}

I implemented this based on an older question, asked here as well: Can't get Windows Overlay icons to work in TListView

To be more precise, this function has been working great for me for over 10 years, but about a year ago I added SHGFI_OVERLAYINDEX as well

The issue, as explained and as can be seen in the video, disappears entirely when I simply and only remove SHGFI_OVERLAYINDEX from the function call. For whatever reason also asking for the overlay icon, causes the weird behaviour.

Even when I completely disable the CN_NOTIFY message subclassing that goes along with this functionality (see: Can't get Windows Overlay icons to work in TListView ) but call SHGetFileInfo() with SHGFI_OVERLAYINDEX, the problem happens !

Does anybody have an idea what this could be ? A solution could be to not ask for the overlay icon on these problematic systems, but then I need to find a way to actually detect that the problem is going to happen (or is happening) somehow.

来源:https://stackoverflow.com/questions/38466574/virtual-listview-items-get-wrong-content-when-hovering-over-them-from-bottom

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!