News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Numeric edit controls that accept negative #s

Started by NoCforMe, December 10, 2011, 12:42:48 AM

Previous topic - Next topic

NoCforMe

In learning how to use edit controls with numbers, I've run into an annoying problem with the way Windoze handles the situation.

One can set up an edit control to accept only numeric input by using the ES_NUMBER style when creating the control. Fine.

The problem is that now, the control really will only accept numbers. If you try to type in a negative number, the thing will beep! at you as soon as you type '-'.

Now, the really annoying thing is that by using up-down controls (aka "spinners--see my previous post here on the subject), you can easily get a negative value into the edit control. You just can't type it in.

(The other annoying thing is that both GetDlgItemInt() and SetDlgItemInt() have parameters that let you handle the control's value as a signed quantity.)

So I'm guessing that if you want an edit control that only accepts numeric input including a possible leading '-', you have to code it yourself. Would this be done via subclassing? intercepting messages and notifications? Surely someone else has already solved this problem and has a nice packaged solution?

bomz

#1
invoke SetWindowLong, ........., GWL_WNDPROC,

Iczelion'а lesson 20
http://win32assembly.online.fr/tut20.html

In few word - you catch all messages for some window (your editcontrol), and filtering it as you want, so your editcontrol may "see" only +-1234567890 for ex
MessageBox

MichaelW

#2
This code uses a subclass procedure to filter the characters. I did not have time to provide support for home, end, backspace, delete, etc, or provide any way to reinitialize the control to prepare it for a new input, or comment the code.

Edit: This version contains the code necessary to pass hHome, end, and Backspace to the original edit control window

;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================
IDC_EDIT equ 1000
;==============================================================================
    .data
        hInstance     dd 0
        hwndEdit      dd 0
        pPrevEditProc dd 0
        fSign         dd 0
        fPoint        dd 0
        charIndex     dd 0
        acceptChars   db "+-.0123456789",8,0  ; Include backspace
    .code
;==============================================================================

EditProc proc hwnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    SWITCH uMsg

        CASE WM_CHAR

            invoke crt_strchr, ADDR acceptChars, wParam
            .IF eax

                sub eax, OFFSET acceptChars

                SWITCH eax

                    CASE 0,1

                        .IF fSign == 0 && charIndex == 0
                            inc fSign
                            inc charIndex
                            jmp call_window_proc
                        .ENDIF

                    CASE 2

                        .IF fPoint == 0
                            inc fPoint
                            inc charIndex
                            jmp call_window_proc
                        .ENDIF

                    CASE 3..12

                        inc charIndex
                        jmp call_window_proc

                    CASE 13

                        jmp call_window_proc

                ENDSW
            .ENDIF

            ;--------------------------------------------------------
            ; Swallow any other keys by returning zero, to indicate
            ; to the caller that the message hasb been processed.
            ;--------------------------------------------------------

            xor eax, eax
            ret

        CASE WM_KEYDOWN, WM_KEYUP

            SWITCH wParam

                CASE VK_HOME, VK_END

                    jmp call_window_proc

            ENDSW

            ;--------------------------------------------------------
            ; Swallow any other keys by returning zero, to indicate
            ; to the caller that the message hasb been processed.
            ;--------------------------------------------------------

            xor eax, eax
            ret

    ENDSW

  call_window_proc:

    invoke CallWindowProc, pPrevEditProc, hwnd, uMsg, wParam, lParam
    ret

EditProc endp

;==============================================================================

DialogProc proc hwndDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    SWITCH uMsg

      CASE WM_INITDIALOG

        invoke GetDlgItem, hwndDlg, IDC_EDIT
        mov hwndEdit, eax

        invoke SetWindowLong, hwndEdit, GWL_WNDPROC, EditProc
        mov pPrevEditProc, eax

      CASE WM_COMMAND

        SWITCH wParam

          CASE IDCANCEL

            invoke EndDialog, hwndDlg, NULL

        ENDSW

      CASE WM_CLOSE

        invoke EndDialog, hwndDlg, NULL

    ENDSW

    return 0

DialogProc endp

;==============================================================================
start:
;==============================================================================

    invoke GetModuleHandle, NULL
    mov hInstance, eax

    Dialog "Test", \
           "MS Sans Serif",10, \
            WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \
            1, \
            0,0,100,75, \
            1024
    DlgEdit WS_BORDER,5,5,87,10,IDC_EDIT

    CallModalDialog hInstance, 0, DialogProc, NULL

    exit

;==============================================================================
end start



And for more complex heeds there is a MaskedEdit control, although I have never tried to use one from MASM code.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa369797(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/aa733654(v=VS.60).aspx
eschew obfuscation

NoCforMe

Thanks, Michael. That (the masked edit control) looks interesting, but I have no idea how you'd implement that in assembly language.

Regarding subclassing an edit control, I had forgotten about all the control characters (backspace, delete, cursor movement, etc.) that have to be handled. Maybe I'm misunderstanding things, but wouldn't it be enough just to filter out the characters you don't want, and let the default handler take care of the rest?

In other words, what if my front-end code simply threw away any characters that weren't 0-9 and '+' or '-', while passing through any control characters? Wouldn't the control then behave as if it had the ES_NUMBER style, except that a hyphen would now be allowed for negative numbers? Or would that be too easy?

Gunner

This is from a tutorial I wrote for another forum for subclassing.  Play around with this.  Modify the SubClassNumbers proc to suit your needs.
~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

NoCforMe

Thanks, that make it very clear, and it seems to be just as I said, you just keep what characters you want and throw the rest away.

Of course, this isn't a perfect solution. For instance, the user can type in something like 928-7654, which the numeric converter will choke on. But it's a pretty good start.

Gunner

You can have a CharCount variable and if it is 1 then allow a minus sign otherwise don't allow it.

or in the subclass, handle WM_KILLFOCUS and when you get that message, validate the number and if it is not valid, inform user, or remove all minus signs from the number except the first one.
~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

donkey

#7
By checking to make sure that the character count is at zero before allowing a "-" you can avoid illegal use of the sign. For example:

// I don't do MASM so this is in GoAsm syntax

// Set up the subclass (usually in WM_INTIDIALOG or WinMain)
// To be compatible with Windows versions lower than XP define CCUSEORDINALS
Invoke SetWindowSubclass, [hEdit], offset NumEditProc, 1, 0

// The subclass procedure
NumEditProc FRAME hwnd,uMsg,wParam,lParam,uIdSubclass,dwRefData
LOCAL buffer[1024]:B
LOCAL pt:POINT

cmp D[uMsg],WM_CHAR
jne >>.DISALLOWPASTE
mov eax,[wParam]

cmp eax,0x1F
jle >>.PROCESSMESSAGE

cmp eax,"-"
je >.PROCESSSIGN

cmp eax,"0"
jl >
cmp eax,"9"
jle >>.PROCESSMESSAGE
:
xor eax,eax
ret

.PROCESSSIGN

// If there is already a - sign then ignore
invoke GetWindowText,[hwnd],offset buffer,256
cmp B[buffer],"-"
je <<

invoke GetCaretPos,offset pt
mov edx,[pt.x]
mov eax,[pt.y]
shl eax,16
mov al,dl
invoke SendMessage,[hwnd],EM_CHARFROMPOS,0,eax
test eax,eax
jnz <<

.DISALLOWPASTE
cmp D[uMsg],WM_PASTE
jne >>.PROCESSMESSAGE
// Get the data from the clipboard
// It will always be in CF_TEXT format
invoke OpenClipboard,[hwnd]
invoke GetClipboardData,CF_TEXT
// NULL return means no useable data
test eax,eax
jz >>.NOCLIPBOARDDATA

// The clipboard manages the returned buffer so copy it to a local one
invoke lstrcpy,offset buffer,eax
// Empty and close the clipboard
invoke EmptyClipboard
// Closing the clipboard invalidates the buffer returned from GetClipboardData
invoke CloseClipboard

// Empty the edit control
invoke SetWindowText,[hwnd],NULL

// Attempt to translate the number
invoke msvcrt.dll:atoi,offset buffer
add esp,4

// convert the resulting number to a string
invoke wsprintf,offset buffer,"%i",eax
add esp,12
// display the number
invoke SetWindowText,[hwnd],offset buffer
xor eax,eax
ret

.NOCLIPBOARDDATA
invoke CloseClipboard
xor eax,eax
ret

.PROCESSMESSAGE

invoke DefSubclassProc, [hwnd], [uMsg], [wParam], [lParam]
ret
endf


I have also removed the WM_PASTE message as it can bypass the WM_CHAR exclusions making them pointless. However you can always write a verification routine to check for invalid numbers being pasted in.

Edgar

Edit: I wrote a quick example of how to verify data pasted into the edit box. It uses atoi and performs a very simple check to eliminate invalid inputs but for the most part should suffice.

Edit: Fixed up PROCESSSIGN to allow the user to insert a "-" later as long as its in position 0.

Final edit: Fixed up a few things to help with keyboard shortcuts and limit some ways to sneak in invalid data
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Here's a project (RadAsm 3) for the code above. I have included the executable in case you don't have GoAsm or my headers. I'd be interested to know if its possible to trick the edit box into taking invalid data, granted I haven't tested it thoroughly so there are probably a few ways but I think most of them are taken care of.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

hutch--

This should come close to doing the job, a subclassed edit control that accepts numbers, the period sign and the minus sign. It changes sign with the + and - keys and will not allow more than 1 period or typing text before the minus sign.

The action is in the last proc which is the subclass for the edit control. I think this one is idiot proof but no garrantees.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

Nice Steve but it allows anything to be pasted into the control. You might consider adding a handler for WM_PASTE in order to filter out unwanted content, for example I had it accept "Edgar" easily enough. Do like the decimal thing though, didn't consider decimal numbers when I threw mine together, might add something to allow a . or ,
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

hutch--

Normally you would exclude the clipboard with a control filter like this as its impossible to reliably format the range of stuff that can be pointed at it.

This seems to work OK.


    .elseif uMsg == WM_PASTE
      xor eax, eax
      ret
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

Well, impossible is a bit of a stretch. Only CF_TEXT is available for pasting into an edit control so any other formats can be ignored since if one is on the clipboard the paste functionality is disabled by Windows. For the actual parsing, I admit to doing it the lazy way but it only requires scanning for a non-numeric character (0..9 as well as . and +/-) then rejecting the paste if one is present. I'll work something out in the morning that will reliably allow for pasting into a numeric only control without my atoi cheat.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Here's a version that verifies the pasted number and allows decimal points. Only one decimal point is allowed, it can be anywhere in the edit control.

EDIT: a slight logical error that allowed a paste of a single non-numeric character if it was in the first position - corrected.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable