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 (http://www.masm32.com/board/index.php?topic=17903.msg150864#msg150864) 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?
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 (http://smiles.kolobok.us/light_skin/wizard.gif)
(http://s2.ipicture.ru/uploads/20111210/q4ByU737.gif)
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
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?
This is from a tutorial I wrote for another forum for subclassing. Play around with this. Modify the SubClassNumbers proc to suit your needs.
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.
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.
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
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
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.
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 ,
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
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.
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.