News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Issues with listboxes

Started by NoCforMe, November 21, 2023, 07:17:02 AM

Previous topic - Next topic

NoCforMe

First of all, I'm happy to see this new board here. I think it's good to have a place where we can focus on issues with the programming environment (Win32) rather than general assembly-language programming questions.

My issue is with the lowly Win32 control, the listbox. This is a dinosaur, relatively speaking, being one of the oldest controls in the OS, going back to Windows 3.1. So you'd think that being that old they would have solved any problems with it. Unfortunately, this seems not to be the case.

I'm working on a project based on a listbox as the main control. It's a big program which brings out all the choices from SystemParametersInfo() to a user interface, basically a mini Control Panel type of app. The listbox contains all the choices.

Since there are so many items the listbox is provided with a vertical scrollbar. Here's the problem: the scrollbar doesn't work properly. At least it doesn't work right at the beginning: oddly, after selecting any item in the listbox, it works OK.

I tried solving this by playing games with selecting items in the listbox after creating it, and with scrolling the listbox (code below). This kinda-sorta helped, but you can see in the two screenshots below that it resulted in a severely messed-up listbox. (Further scrolling would clear up this mess.)

Here's the code I used to try to fix the problem, which didn't really work:
; Attempt to set the listbox selection to the 2nd item, then remove the selection:
    INVOKE    SendMessage, SPIlistboxHandle, LB_SETCURSEL, 1, 0
    INVOKE    SendMessage, SPIlistboxHandle, LB_SETCURSEL, -1, 0

; Attempt to fix scrolling problem by manually scrolling listbox:
; First get current scroll info:
    MOV    sii.cbSize, SIZEOF SCROLLINFO
    MOV    sii.fMask, SIF_ALL
    INVOKE    GetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii

    MOV    sii.nPos, 0
    INVOKE    SetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii, FALSE
    MOV    sii.nPos, 9999
    INVOKE    SetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii, FALSE
    MOV    sii.nPos, 0
    INVOKE    SetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii, TRUE
    MOV    sii.nPos, 1
    INVOKE    SetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii, FALSE
    MOV    sii.nPos, 0
    INVOKE    SetScrollInfo, SPIlistboxHandle, SB_VERT, ADDR sii, TRUE

You can see from the listbox that it's not a standard usage; it's an owner-draw control where I split it into two columns. This is simply done through the WM_DRAWITEM handler. The only other fancy processing I do is to handle LBN_SELCHANGE so I know when the user selected an item. I thought maybe the owner-draw stuff was causing the scroll issues, but when I removed that flag (LBS_OWNERDRAWFIXED), the problem persisted, so I'm pretty sure that's not the cause of the problem here.

The problem is that when the program opens, you can scroll by moving the scroll "thumb", but not by either top or bottom button nor by clicking in the blank space (which is supposed to scroll by "pages"). But once you select any item by clicking on it, all the scrollbar parts work fine. Try it in the attached .EXE.

It's not a show-stopper but it's quite annoying. If anyone has any ideas, would like to hear them. Also any comments about the user interface would be welcome. I know it's non-standard but I'm hoping it can be somewhat intuitive. (And the help screens should make it easy to use.)

There are also issues with SystemParametersInfo() itself, which I'll address in another posting later.

I'm not posting code to this because it's quite long and complicated. Maybe I'll post it when (and if) the project is ever finished ...
Assembly language programming should be fun. That's why I do it.

NoCforMe

Well, apparently the problem is ... something I'm doing in my code, because I coded up this little testbed with a listbox, and its scrollbars work perfectly from the get-go.

Wonder what it is that I'm doing that's causing the trouble? All I'm doing, so far as the listbox is concerned, is 1) creating items in it:
; Create the listbox item:
    MOV    EAX, OFFSET LBstringBuffer
    INVOKE    SendMessage, SPIlistboxHandle, LB_ADDSTRING, 0, EAX

; Store a pointer to the SPI_ENTRY in the listbox item:
    INVOKE    SendMessage, SPIlistboxHandle, LB_SETITEMDATA, EAX, EBX

2) Getting the selection in response to a LBN_SELCHANGE notification:
; lParam has the handle to the listbox:
    INVOKE    SendMessage, lParam, LB_GETCURSEL, 0, 0
; Get pointer to SPI entry for this item:
    INVOKE    SendMessage, lParam, LB_GETITEMDATA, lbCurrSel, 0
; Get the item RECT for items where I position a child listbox over them:
    INVOKE    SendMessage, lParam, LB_GETITEMRECT, lbCurrSel, ADDR gpRect

3) Modify the listbox item if the user changes the setting. This involves deleting the item and re-creating it (and restoring its data content):
    INVOKE    SendMessage, lbHandle, LB_DELETESTRING, lbCurrSel, 0
    INVOKE    SendMessage, lbHandle, LB_INSERTSTRING, lbCurrSel, ADDR buffer2
(but this only happens if you modify the item; the problem happens even without doing that)

4) Getting text from the listbox item in the WM_DRAWITEM handler:
    INVOKE    SendMessage, [EBX].DRAWITEMSTRUCT.hwndItem, LB_GETTEXT, [EBX].DRAWITEMSTRUCT.itemID, ADDR buffer0

Don't see how any of those should interfere with the operation of the scrollbar. But obviously I'm doing something to mess it up ...

Take a look at attached program (source included).

Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: NoCforMe on November 21, 2023, 08:10:19 AMWell, apparently the problem is ... something I'm doing in my code

Yep, I got that part of it right.

Turned out the solution was dead simple: call DefWindowProc() for any messages I don't handle. That solved everything. All the listbox scroll functions work perfectly now.

Will post complete program soon.
Assembly language programming should be fun. That's why I do it.

HSE

Hi NoC!

Quote from: NoCforMe on December 04, 2023, 03:40:14 PMTurned out the solution was dead simple: call DefWindowProc()

That it's not a solution: is the first step when subclassing anything.

I don't have a count how many times I made that mistake  :biggrin:

HSE
Equations in Assembly: SmplMath

jj2007

Quote from: HSE on December 04, 2023, 10:22:15 PM
QuoteTurned out the solution was dead simple: call DefWindowProc()

That it's not a solution: is the first step when subclassing anything.

Sure? I normally use invoke CallWindowProc, opControl, hwnd, uMsg, wParam, lParam

LaVolpe (he knows his stuff):
QuoteRe: CallWindowProc vs. DefWindowProc
If you'll notice, the CallWindowProc API has an additional parameter for the previous window procedure. This is an indication that the original procedure (default window procedure) is subclassed. Any messages not processed by an application for any window in the application should be sent to the default window procedure, per MSDN. Every window has a default window procedure that is identified when the class is registered (RegisterClass API) or created from a registered class (CreateWindow API).

If calling DefWindowProc , you are bypassing any subclassed procedures. As mentioned, you typically do not send messages, willy-nilly, directly to the DefWindowProc without understanding any known consequences. Most subclass procedures are subclassing for a reason: to modify the behavior of the window. If you bypass that subclass, then whatever modifying/tracking mechanisms, that subclass may be using, will be skipped

HSE

Quote from: jj2007 on December 04, 2023, 11:24:31 PMI normally use invoke CallWindowProc, opControl, hwnd, uMsg, wParam, lParam

:thumbsup: Yes, a "Dispatch Event" for the class.

I don`t know what LaVolpe knows  :biggrin:  (nor LaVolpe)

Tipical setting is:

mov xax, wParam
.if ax == your_event
    your_event_process
    xor eax,eax 
.else
    invoke Dispatch_event, dispatch_arguments...
.endif

Equations in Assembly: SmplMath

NoCforMe

Now we're getting all confused here, at least as relates to my code which was the topic under discussion. (I don't mind going off on tangents at all, but I do mind confusion.)

My dialog procedure here is not subclassed. Some of the controls within that dialog are subclassed, but the use of DefWindowProc() has nothing to do with them since we're talking about the dialog procedure, not the subclassed child window proc.

I do use CallWindowProc() for any unhandled messages in the subclassed procs, of course. It's the only way to keep the chain of procedures intact. But that's not the issue here.

Does that make sense? Let's not spin ourselves into more needless confusion here.
Assembly language programming should be fun. That's why I do it.

NoCforMe

To (hopefully) clear this up a little, here's what I believe to be the proper way of doing things with dialog procedures and subclassed window procs:

Dialog procedure (or standard, non-subclassed window procedure):
DialogProc    hWin, uMsg, wParam, lparam

    CMP    uMsg, <some messge>
    JE    <process that message>
    CMP    uMsg, <some other message>
    JE    <process that message>
    . . . . .
    INVOKE    DefWindowProc, hWin, uMsg, wParam, lparam

Subclassed window procedure:
SubclassedProc    hWin, uMsg, wParam, lParam

    CMP    uMsg, <some message>
    JE    <process that message>
    . . . . .
    INVOKE    CallWindowProc, oldWindowProc, hWin, uMsg ...

Amiright? Any issues with this?

I still have no friggin' idea when one should use DefDlgProc(). So I just won't use it ...
Assembly language programming should be fun. That's why I do it.