News:

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

Main Menu

Dialog box programming considerations

Started by kkurkiewicz, October 23, 2023, 02:18:34 AM

Previous topic - Next topic

kkurkiewicz

I hope it is not off-topic, but I have a few general questions about dialogs that may seem straightforward, but the official documentation is so lacking that I was unable to find any really useful information there. Could you please help me?

First, what's the best way of responding to button clicks if the main (and only) window of the program is a simple dialog box? Usually, all I do to determine which button raised the WM_COMMAND notification message after I receive one is inspect the value of WPARAM, but apparently, given the dialog definition shown below, checking just WPARAM is not sufficient to define the event definitely because the integer identifier of my Copy button (2) happens to have the exact same value as the constant IDCANCEL (WinUser.h), which is sent to me through WM_COMMAND every time the user presses the Esc key, as described here. I tried examining the high-order word of the WPARAM parameter also for the value BN_CLICKED, but the in-memory value of the parameter seems to always be 02 00 00 00, which – at least to me – makes the messages identical. Should I simply treat all the IDs defined in the Dialog Box Command IDs section of WinUser.h (0 through 7) as system-reserved and just use some other values?

Second, according to this (official) description of the predefined edit control class, all "[e]dit controls use the fixed-pitch font", but no matter how I actually initialize the dialog, the font of my edit field is always the same as the font that's used for labels, and the only way to change it is by using LOGFONT and CreateFontIndirectA. Is this normal? And what is the default font?

Third, is it also normal that the button mnemonics "C", "N", "x" become inactive after I click somewhere inside the edit field? (Please note that the "style" argument of the edit field does not declare WS_TABSTOP.)

DIAL1 DIALOG 0, 0, 270, 100
STYLE WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
FONT 8, "MS Sans Serif"
CAPTION "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{
    CONTROL "", 1, "edit",
            ES_LEFT | ES_READONLY | WS_BORDER | WS_CHILD | WS_VISIBLE,
                    10, 20, 250, 12

    CONTROL "&Copy", 2, "button",
            BS_CENTER | BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP | WS_VISIBLE,
                    10, 50, 40, 12

    CONTROL "&New", 3, "button",
            BS_CENTER | BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP | WS_VISIBLE,
                    60, 50, 40, 12

    CONTROL "E&xit", 4, "button",
            BS_CENTER | BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP | WS_VISIBLE,
                    110, 50, 40, 12
}
Kamil

zedd

When creating dialog box controls, generally I try to avoid using single digit identifiers. Where you use 1, 2, 3, etc. I would use 1001, 1002, 1003, etc. for example. This may help for that part. You don't have to start with "1".

I'll get back to this, once I am back at my computer later today... right now, I have to do some work outside.

kkurkiewicz

#2
Quote from: kkurkiewicz on October 23, 2023, 02:18:34 AMThird, is it also normal that the button mnemonics "C", "N", "x" become inactive after I click somewhere inside the edit field?

What I meant was, "Third, is it also normal that the button mnemonics "C", "N", "x" become inactive after I click somewhere inside the edit field, even if the field is declared to be read-only?"
Kamil

fearless

#3
Could try setting up accelerators to catch the ALT+key for controls then

Have to handle the loading of them first then change the message loop to catch them and process them, something like:

.code

start:

    Invoke GetModuleHandle, NULL
    mov hInstance, eax
   
    invoke LoadAccelerators, hInstance, ACCTABLE
    mov hAcc, eax
   
    Invoke GetCommandLine
    mov CommandLine, eax
    Invoke InitCommonControls
    mov icc.dwSize, sizeof INITCOMMONCONTROLSEX
    mov icc.dwICC, ICC_COOL_CLASSES or ICC_STANDARD_CLASSES or ICC_WIN95_CLASSES
    Invoke InitCommonControlsEx, Offset icc
   
    Invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
    Invoke ExitProcess, eax

then in WinMain change the loop to:

    Invoke CreateDialogParam, hInstance, IDD_DIALOG, NULL, Addr WndProc, NULL
    mov hWnd, eax
    Invoke ShowWindow, hWnd, SW_SHOWNORMAL
    Invoke UpdateWindow, hWnd
    .WHILE TRUE
        invoke GetMessage, addr msg, NULL, 0, 0
        .BREAK .if !eax

        Invoke TranslateAccelerator, hWnd, hAcc, addr msg
        .IF eax == 0
            Invoke IsDialogMessage, hWnd, addr msg
            .IF eax == 0
                Invoke TranslateMessage, addr msg
                Invoke DispatchMessage, addr msg
            .ENDIF
        .ENDIF
    .ENDW

Accelerators are defined and stored as in resource files, and processed via the WM_COMMAND just like any other dialog resource. I define mine as starting with ACC_ and react to those keypresses as if the user clicked the menu option that they correspond to (or button or other control etc)

I have an example of using them in my CD - Compress-Decompress Utility: https://github.com/mrfearless/CD/blob/main/CD.asm



Greenhorn

The problem is that the edit control keeps the focus and in modeless dialog boxes that prevents the use of the Alt-Key as long as the edit control has the focus, as it seems.

Do you need the focus (Caret) in the edit control, e.g. to select text?
Otherwise you can try to set the WS_DISABLED style for the edit control.

Or you use a static control instead of an edit control. If you need the text of a static control to be copied to the clipboard, you need Win Vista or higher and set the SS_NOTIFY style to it. Double-clicking the text copies it to the clipboard.
Kole Feut un Nordenwind gift en krusen Büdel un en lütten Pint.

NoCforMe

I actually have quite a bit of experience using dialogs, so I might be able to help out here. Just to address one of your several points:

Quote from: kkurkiewicz on October 23, 2023, 02:18:34 AMSecond, according to this (official) description of the predefined edit control class, all "[e]dit controls use the fixed-pitch font", but no matter how I actually initialize the dialog, the font of my edit field is always the same as the font that's used for labels, and the only way to change it is by using LOGFONT and CreateFontIndirectA. Is this normal? And what is the default font?

First of all, what was written about edit controls [always] using a fixed-pitch font is simply untrue. Edit controls use whatever font they are set to use. So far as the "default" font goes, that is whatever is assigned by the dialog manager when the dialog is created. That font will be used throughout the dialog, for any controls including static text ones. I'm not sure just how you're creating your dialogs (using a resource editor/compiler, which is the usual way, or creating them from an in-memory template, which is the way I do it), but one of the settable parameters of any dialog is the font to be used. This is set by including the DS_SETFONT style in the dialog style, and specifying the font in a member of the DLGTEMPLATE structure. (These are implementation details that you may or may not need to concern yourself with. If you're using resource files, the font will be one of the things you can set using them.)

So if you use, say Segoe UI, 9 pt. for your dialog as I do, then your edit controls will be initialized to use that font.

You can always explicitly set the font for any control using the WS_SETFONT message, of course, but that shouldn't be necessary if you select the font you want for the dialog in the first place, unless you want different fonts for "labels" (static text controls) and other controls.

BTW, at least on my setup (Windows 7), the default font if you create an edit control (or any other control for that mattter) outside of a dialog using CreateWindowEx() is UGLY.
Assembly language programming should be fun. That's why I do it.

NoCforMe

#6
Regarding your problems determining which control sent a message, what brother Zedd said here: don't assign any control the same value as any other control in the dialog. All control IDs used in a dialog should be unique, at least within that dialog. If you assign IDOK to a control, that's it: no other control should have that same ID. That way there's no ambiguity about who sent a message; you can always rely on the control ID (in the low word of wParam for WM_COMMAND).

My convention is to start all my control IDs (at least the ones I will be checking for WM_COMMAND messages) at 1200. I use IDs starting at 100 for all my static text controls ("labels"), which I don't care about after creating them. Just make sure they're unique!
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: kkurkiewicz on October 23, 2023, 02:18:34 AM...because the integer identifier of my Copy button (2) happens to have the exact same value as the constant IDCANCEL (WinUser.h), which is sent to me through WM_COMMAND every time the user presses the Esc key

This is by design: you use IDOK for your Ok button, and IDCANCEL for your Cancel button. Pressing the Esc key is the same as clicking on the Cancel button.

Pseudocode:
DlgControl dcButton, "OK", BS_DEFPUSHBUTTON or WS_TABSTOP, ..., IDOK
DlgControl dcButton, "Quit", BS_PUSHBUTTON or WS_TABSTOP, ..., IDCANCEL

Sorry, this is in strong contradiction to what NoCForMe wrote a minute ago, but I've tested that a hundred times...

Quote from: zedd151 on October 23, 2023, 02:40:34 AMWhen creating dialog box controls, generally I try to avoid using single digit identifiers. Where you use 1, 2, 3, etc. I would use 1001, 1002, 1003, etc.

As written above, there is no conflict: you should use IDOK for the OK button, and IDCANCEL for the Cancel button. Besides, as a rule, IDs below 128 use much less storage in the rsrc section (for those who are code size aware :smiley: )

NoCforMe

JJ, not sure how this contradicts what I wrote. (Not taking exception here and certainly willing to be proven wrong!) All that you wrote is true. Where's the problem?

I should have written "don't assign any control the same value as IDOK or IDCANCEL unless you want that control to have the same effect as IDOK or IDCANCEL".
Assembly language programming should be fun. That's why I do it.

jj2007

Well, you wrote "don't assign any control the same value as IDOK or IDCANCEL"... perhaps you meant "don't use IDOK more than once"?

NoCforMe

#10
Gotcha.

To clarify: Yes, use IDOK and IDCANCEL for those two important dialog-box functions (usually the "OK" and "Cancel" buttons, though you can use these any way you like, although you might want to be careful about using them in non-standard ways for the sake of your users). Just don't use these same IDs for any non-IDOK or IDCANCEL controls.

If you do use IDOK or IDCANCEL (or any other ID for that matter) for more than one control, you're going to have a hard time determining just which control sent that message**. If you want two controls to have the same functionality as IDOK, say, but want to be able to easily tell which one sent the message, then you might want to assign a different ID to the second control but have it perform the same function as the IDOK one. Does that make sense?

BTW, I must commend you for the quality (and on-topic-ness) of your questions. Excellent questions!

** You can distinguish controls with the same ID by their control handles, which arrive in lParam for WM_COMMAND. Just get their handles in your dialog initialization (WM_INITDIALOG) handler using GetDlgItem() and store them as globals.

Aaaaargh. That won't work. What was I thinking? You can't get two different handles for two controls with the same ID. (What'll probably happen is that you'll get the handle of the first control in the dialog both times.)

Moral of this story: don't have duplicate control IDs in a dialog.
Assembly language programming should be fun. That's why I do it.

kkurkiewicz

All makes sense, thank you.

When it comes to whether I use templates or RC, I habitually put all dialogs and other resources in an all-in-one resource script and then load them using functions such as DialogBoxParamA, so, as this is also how I handle the example dialog shown in my first post, I'll just leave it as it is. And regarding question three – if I don't find any way to stop the system from changing the color of the font from black to gray, I will redefine the edit box as a static control, as suggested by Greenhorn :thumbsup:
Kamil

kkurkiewicz

And regarding question one, I'll just go with 1001, 1002....
Kamil

NoCforMe

Static controls make good "output" (display) fields, and you can easily change the text they show with either SetDlgItemText() or SetWindowText(). Only thing I don't like about using them is that with edit controls (set to read-only), you can easily select and copy the text. There's a way to set up statics so you can copy their text by double-clicking them but it's a PITA.

Have you tried adding the WS_TABSTOP style to the edit field to fix your accelerator-prefix problem? I never use those so unfortunately can't help you much there.
Assembly language programming should be fun. That's why I do it.

kkurkiewicz

Quote from: NoCforMe on October 24, 2023, 09:45:37 AMHave you tried adding the WS_TABSTOP style to the edit field to fix your accelerator-prefix problem?

I think that, at least for now, I'll just get rid of the three mnemonics and remove the Copy button because forcing the user to click a designated button just to select some text seems completely unreasonable to me now. Initially, I wanted the dialog to look like the one shown below, but I would still like the "Result" string to be selectable. Specifying WS_TABSTOP doesn't work because every time the input focus is transferred to the edit field, Windows stops interpreting the characters "C", "N", "x" as hotkeys.


Kamil