Changing the step size of an up-down control
by Richard Russell, July 2014
When you create an up-down control you can easily choose the minimum and maximum values, and the direction in which it changes (i.e. whether clicking on the up arrow increases or decreases the value). However you cannot choose the step size: each click always changes the value by either +1 or -1 (or zero if the limit has been reached).
To modify the behaviour of the up-down control requires a small piece of assembler code. The simplest option is to change the step size to a value other than one; the code example below demonstrates setting it to five:
REM!WC UDN_DELTAPOS = -722 WM_NOTIFY = 78 ES_NUMBER = 8192 UDM_SETRANGE = 1125 INSTALL @lib$+"WINLIB2" dlg% = FN_newdialog("test1",100,100,100,100,8,200) PROC_editbox(dlg%,"50",101,10,10,50,10,ES_NUMBER) PROC_dlgctrl(dlg%,"",102,0,0,0,0,&50000096,"msctls_updown32") PROC_showdialog(dlg%) PROCsetdelta(!dlg%, 102, 5) SYS "SendDlgItemMessage", !dlg%, 102, UDM_SETRANGE, 0, (0 << 16) + 100 SYS "SetDlgItemText", !dlg%, 101, "40" ON ERROR PROC_closedialog(dlg%):PRINT REPORT$:END ON CLOSE PROC_closedialog(dlg%):QUIT REPEAT WAIT 1 UNTIL FALSE PROC_closedialog(dlg%) END DEF PROCsetdelta(hdlg%, udid%, delta%) LOCAL D%,M%,O%,P% PRIVATE C%,L% : IF C%=0 DIM C% 100, L% -1 P% = C% SYS "GetWindowLong", hdlg%, -4 TO O% [OPT 10 .D% pop eax : push O% : push eax : jmp "CallWindowProc" .M% cmp dword [esp+8],WM_NOTIFY : jnz D% mov edx,[esp+16] : cmp dword [edx+4],udid% : jnz D% cmp dword [edx+8],UDN_DELTAPOS : jnz D% mov eax,[edx+16] : imul eax,delta% : mov [edx+16],eax xor eax,eax : ret 16 ] SYS "SetWindowLong", hdlg%, -4, M% ENDPROC
The procedure PROCsetdelta takes three parameters: the handle of the dialogue box, the ID of the up-down control and the desired step size.
However this may not always be what you want. Suppose that the contents of the associated edit box are initially 1 rather than 0 (for example because the user has typed in that value). Now, when you click the up button, the value will change to 6, then 11, then 16 and so on. You might prefer that instead of adding (or subtracting) 5, the up-down control changes the value to the next multiple of 5, so that the first click will change it from 1 to 5 (rather than 6). That can be achieved using this slightly more complicated code:
REM!WC UDN_DELTAPOS = -722 WM_NOTIFY = 78 ES_NUMBER = 8192 UDM_SETRANGE = 1125 INSTALL @lib$+"WINLIB2" dlg% = FN_newdialog("test1",100,100,100,100,8,200) PROC_editbox(dlg%,"50",101,10,10,50,10,ES_NUMBER) PROC_dlgctrl(dlg%,"",102,0,0,0,0,&50000096,"msctls_updown32") PROC_showdialog(dlg%) PROCsetdelta(!dlg%, 102, 5) SYS "SendDlgItemMessage", !dlg%, 102, UDM_SETRANGE, 0, (0 << 16) + 100 SYS "SetDlgItemText", !dlg%, 101, "41" ON ERROR PROC_closedialog(dlg%):PRINT REPORT$:END ON CLOSE PROC_closedialog(dlg%):QUIT REPEAT WAIT 1 UNTIL FALSE PROC_closedialog(dlg%) END DEF PROCsetdelta(hdlg%, udid%, delta%) LOCAL D%,M%,O%,P% PRIVATE C%,L% : IF C%=0 DIM C% 100, L% -1 P% = C% SYS "GetWindowLong", hdlg%, -4 TO O% [OPT 10 .D% pop eax : push O% : push eax : jmp "CallWindowProc" .M% cmp dword [esp+8],WM_NOTIFY : jnz D% mov edx,[esp+16] : cmp dword [edx+4],udid% : jnz D% cmp dword [edx+8],UDN_DELTAPOS : jnz D% mov eax,[edx+12] : mov ecx,delta% : xor edx,edx : idiv ecx : mov ecx,edx mov edx,[esp+16] : imul ecx,[edx+16] bt ecx,31 : adc eax,[edx+16] : imul eax,delta% sub eax,[edx+12] : mov [edx+16],eax xor eax,eax : ret 16 ] SYS "SetWindowLong", hdlg%, -4, M% ENDPROC