Hi All,

I'm trying to save the text from a Virtual Listview. Rather than create a fully separate routine for this, I'd like to tap into the WM_NOTIFY message that is normally called for Redrawing all the *ON-screen* items and subitems (rows/columns) in a VL.

A Virtual Listview is a wonderful thing, it handles thousands of entries with ease which would choke a regular listview dead. Unfortunately, many of the common listview commands as well as several of the fancy new comctl32 display features aren't supported.

To create a "virtual" listview you specify the LVS_OWNERDATA style when creating the listview, then send a LVM_INSERTITEM message for each item/subitem you want displayed. When you invoke RedrawWindow or use SendMessage with LVM_UPDATE, this triggers WM_NOTIFY to be called with the LVN_GETDISPINFO notification code for each row Item that is on-screen at the time.

Within the WM_NOTIFY message you fill the pszText field of the LV_ITEM structure with your text for each subitem, something like:

.IF wmsg == WM_NOTIFY
mov edi, lparam
assume edi:ptr NMHDR

mov eax, hListView

.if [edi].hwndFrom == eax && [edi].code == LVN_GETDISPINFO

assume edi:ptr LV_DISPINFO

.if [edi].item.imask & LVIF_TEXT

.if [edi].item.iSubItem == 0

; place your text in a buffer then save it to pszText
mov [edi].item.pszText, offset StringBuff

.elseif [edi].item.iSubItem == 1

...ETC for each column iSubItem

WM_NOTIFY will be called again for the next row Item...BUT, only for the number of items that are displayed in the Virtual listview. If you resize the listview to only display 2 items, then WM_NOTIFY will only be called twice (less duplicate messages).

What I would like to be able to do is force WM_NOTIFY messages for the number of rows that *I* want to be processed, not just what Windows wants to send. I don't care that they're not all displayed, but I want to tap into the loop to handle what is in each .item.pszText as it comes and move each string into a GlobalAlloc memory buffer (along with tabs and CR/LF) prior to saving the whole thing with WriteFile. I can do this successfully, but sure enough as fate would have it, it only saves the number of rows that are displayed.

There's actually quite a bit of code that's already nicely integrated into the WM_NOTIFY routines to generate the proper text for the listview items, so the easy way out to save the text would be to get the routines to run for the full number of items I have, not just what you can see at any one time.

One thing I thought of was to cause a vertical scroll of the listview to the end, thinking this might get WM_NOTIFY called for each row. I'm not sure if this is even possible and haven't really looked into it much yet. The other apparent solution is to send the WM_NOTIFY message explicitly. This seems to be where I'm having problems getting it right.

Before I even see if this is possible, I need to get the syntax and preparatory work correct for explicitly sending a WM_NOTIFY message. I figured the first step would be to fill the NMHDR structure associated with the message, then use a loop to send the notification code LVN_GETDISPINFO for as many times as desired. Something like

mov edi, offset hdr
assume edi:ptr NMHDR

mov eax, hListView
mov [edi].hwndFrom, eax
; mov [edi].idFrom, ??
mov [edi].code, LVN_GETDISPINFO

.while lvi.iItem < number rows to process

invoke SendMessage, hListView, WM_NOTIFY, NULL, edi
inc lvi.iItem

First problem - assume edi:ptr NMHDR
Normally lparam from a message becomes edi, in this case I have to specify my own structure, but I also have to take into account the LV_DISPINFO structure that is required, which is also linked to the LV_ITEM structure, i.e. they are used here:

&& [edi].code == LVN_GETDISPINFO
assume edi:ptr LV_DISPINFO
.if [edi].item.imask & LVIF_TEXT
.if [edi].item.iSubItem == 0 ; LV_ITEM

I'm guessing I need to emulate the complete linked list of structures, then fill in all necessary fields before the WM_NOTIFY message has any chance of success. Since all the structures are referenced from the initial edi of NMHDR, I was hoping stacking them in the .data section would work to create a stable multi-structure memory location.

The other thing I'm wondering about is the WM_NOTIFY message itself.
WM_NOTIFY message informs the parent window of a control that an event has occurred in the control or that the control requires some kind of information.

The "parent window" of a control. Well my control is a listview, it's parent is an edit control, and the whole thing is subclassed. In the "real" WM_NOTIFY routine called when the window is redrawn, the SysListView32 class (hListView) is being handled. I need to ensure that that is the window that is receiving the message.

Initially I subclassed it to handle the WM_CONTEXT message to create a right click Popup menu, worked great, all went to hell when I went Virtual! This is another problem with Virtual Listviews I'm struggling with. One of the few notification messages the VL still handles is LVN_ITEMCHANGED. I can use it to detect a change, but it's not really suitable for initiating a Popup menu. NM_RCLICK doesn't seem to respond well so I may have to go to some mouse capture method, not really what I want to start messing with ;-)

I won't even bother describing trying to piece together C, Delphi and VB code examples to use NM_CUSTOMDRAW to colorize a virtual listview item...it would be too painful to recount ;p

Anyway, if this made any sense, and anyone has any thoughts on it I would be glad to hear it. The Virtual listview seems to generate a lot of questions from people in newsgroups on how to use it properly. While the questions seem to revolve around a lot of the same issues, like "How the hell do I get those VL handling messages to work?", I haven't seen much in general about explicitly sending a WM_NOTIFY message in this way (at least in a language I can fully understand ;)

Thanks for reading,

Posted on 2002-07-04 18:59:37 by Kayaker
I think we could publish a book from your post.
Posted on 2002-07-04 21:52:54 by comrade
Lol, yeah well, some of the chapters are missing, that's the problem. I guess I should ask a more specific question in order to narrow it down.

I know some people here are familiar with Virtual listviews, which is where I got some of my information from, and thought they may have encountered similar problems. One problem I want to address is if it's possible to generate a right click popup menu on a VL. Since some of the normal notification messages like WM_CONTEXTMENU, WM_RBUTTONDOWN, NM_RCLICK, even WM_DROPFILES, no longer trigger on my virtual listview I need to find a work-around.

While the VL doesn't sense these messages, but is itself subclassed, I thought someone might have an idea if you could still use the messages somehow via the parent, or by un-subclassing or some other trick.

I wanted to describe WHY I want to be able to explicitly send a WM_NOTIFY message, which is why I went into the VL shpiel, but it's really more of a general question. CAN you do this? It doesn't have to specifically be the LVN_GETDISPINFO notification message being sent, any will do. I just want to make sure that my syntax, logic of using the function, and supplying the correct structures is correct.

invoke SendMessage, hListView, WM_NOTIFY, idCtrl, pnmh(LPNMHDR)

Hope this makes a bit more sense.

Posted on 2002-07-05 02:32:22 by Kayaker
Haven't translated all of your post yet (wasnt that good in english at school), but at least I know that NM_RCLICK does work with virtual listviews. So there is no need for any workaround.
Posted on 2002-07-05 02:50:35 by japheth