练习002 - 使Input控件只能输入特定范围内的数字
本帖最后由 pusofalse 于 2010-2-26 23:38 编辑出题目的:
1、认识Windows消息的传递流程。
2、如何处理截取到的消息。
题解:
如题目所说,用GUICtrlCreateInput创建一个input输入框,使其只能输入数字,并且只能在1-50之间。以下是不合法的情况举例:
A_b2.# —— 字母、数字、特殊字符的任意组合。
010 —— 首位为0。
51、1001—— 不在1-50范围内。
要求:
1、禁止生成任何临时文件,禁止调用任何第三方。
2、无。
拓展:
如果完成了上述基础要求,再使input输入框有如下特性:
0、同是在1-50之间的数字。
1、用户在输入某个不合法的字符时(如字母a)或数字超出范围时,在输入那一刻(一刻——时长为无限短),使其不会在input框中显示,而是直接阻止输入(注,之前已经输入的合法字符不能删除,类似“日期和时间 属性”中的显示时间的input控件,在输入“25”时只会显示“2”,输入“5”时,没有任何反应,“5”在输入的那一刻,并没有在输入框中显示出来再删除掉,即,不会存在闪屏现象)。
2、使用ControlSetText、ControlSend、ControlGetText设置、读取input框内容时,编程使这3个函数运行失败,即不能设置读取到任何内容,input中的内容只能用物理输入。(类似某些游戏的输入框,不能使用ControlSend一样的情况)。
3、禁止输入框的粘贴、剪切功能。
4、除上述3点之外,使input框的其他特性不受影响。
加分:
视思路加分,20-90分不等。
1、如果完成基础要求,分值20-30。
2、如果完成拓展中的全部要求,分值70-90。 #include <WinAPI.au3>
If not IsDeclared("WM_CHAR") Then CONST $WM_CHAR = 0x102
If not IsDeclared("WM_PASTE") Then CONST $WM_PASTE = 0x302
If not IsDeclared("WM_SETTEXT") Then CONST $WM_SETTEXT = 0xc
$hGUI = GUICreate("Test", 200, 100)
$iIptTest = GUICtrlCreateInput("", 20, 20, 160, 20)
$hIpt = GUICtrlGetHandle(-1)
$hIptProc = DllCallbackRegister("_IptProc", "int", "hWnd;uint;wparam;lparam")
$pIptProc = DllCallbackGetPtr($hIptProc)
$hOldProc = _WinAPI_SetWindowLong($hIpt, -4, $pIptProc)
GUISetState()
Do
Until guiGetMsg() = -3
Func _IptProc($hWnd, $iMsg, $wParam, $lParam)
ConsoleWrite("0x" & Hex($iMsg) & @CRLF)
If $iMsg = $WM_CHAR Then
;若限制只能输入英文和数字,把$wparam < 128 改为StringRegExp(Chr($wparam), "") = 0
If $wparam <> 8 And StringRegExp(Chr($wparam), "") = 0 Then
_WinAPI_MessageBeep(2)
Return False
EndIf
ElseIf $iMsg = $WM_PASTE Then
If StringRegExp(ClipGet(), "[\x20-\x7f]") Then Return False
ElseIf $iMsg = $WM_SETTEXT Then
_WinAPI_MessageBeep(2)
Return False
EndIf
Return _WinAPI_CallWindowProc($hOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc ;==>_IptProc()这是以前论坛里前辈发的收藏的,呵呵,稍微测试了一下,退出又点问题 本帖最后由 pusofalse 于 2010-1-2 20:04 编辑
回复 2# llztt
感谢llztt兄的参与,但收藏的就不要这么早就贴上来了。看到一次现成的代码,会容易产生思维定向。暂时屏蔽,等有其他确切方案之后再解除屏蔽,望理解。 本帖最后由 llztt 于 2010-1-2 19:54 编辑
不好意思啊,呵呵,直接贴上确是不妥,有违开贴主旨了,我会下次注意了
关于主题,我想最难的事是如何截获(不是监听)按键的动作,这个除了API或第三方还真没见出其他办法,至于字符限制什么的倒简单多了,呵呵,收藏过两个关于按键的源码,用的关键代码都一样。。
关于数值要求的<50有些麻烦啊。大于100判断好弄,但<50这个什么时候判断呢,因为我们输入67时,先输入的肯定是6,若判断这个6小于50就return的话,肯定不对了吧。。。难道是在失去焦点或按提交按钮才判断是否小于50? 基础的是不是这样?#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
$Form1 = GUICreate("Form1", 256, 105, -1, -1)
$Input1 = GUICtrlCreateInput("", 24, 40, 190, 21, BitOR($ES_AUTOHSCROLL,$ES_NUMBER))
GUICtrlSetLimit (-1,3,1)
GUISetState(@SW_SHOW)
While 1
Sleep (10)
$Read = GUICtrlRead ($Input1)
IF StringIsDigit ($Read) = 0 Then GUICtrlSetData ($Input1,"")
IF StringLeft ($Read,2) <> 10 Then
IF StringLen ($Read) = 2 Then
IF $Read < 50 Then GUICtrlSetData ($Input1,"")
EndIf
Else
IF $Read > 100 Then GUICtrlSetData ($Input1,"")
EndIf
$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
Exit
EndSwitch
WEnd
本帖最后由 lchl0588 于 2010-1-2 21:16 编辑
呵呵,AU3 偶是一只小小的菜鸟,不能和楼上的比,只交一个有片面的小小的作业吧:Dim $a=InputBox("作业","请输入1到50","1到50","","",20,500,200,0)
if $a>1 And $a<50 Then
If $a>1 & $a<50 Then
MsgBox(0,"作业","恭喜你输对了",2)
EndIf
EndIf你有你的表达方式 ,偶有最菜最基础的写法 呵呵!! 抱歉,这个题目本身有问题。
我已经修改题目了,4# llztt兄、5# landays兄,让两位白白思考了,抱歉至极! 本帖最后由 afan 于 2010-1-3 01:28 编辑
这个貌似只能截取系统消息再处理了 东抄西拼,只能做到输入框不能用右键粘贴,不能能禁止键盘的快捷键,可以试下在当前GUI上注册CTRL + V 类的快捷键,但恐怕那样没意思了,并不是真正意义上的禁止.
代码有些乱七八糟,高手莫见笑!!!#include <WindowsConstants.au3>
#include <Constants.au3>
#include <GuiMenu.au3>
#include <WinAPI.au3>
$Form1 = GUICreate("只能输入1-50示例", 334, 98, -1, -1, $WS_THICKFRAME, -1)
$Input1 = GUICtrlCreateInput("", 112, 32, 121, 21)
$wProcNew = DllCallbackRegister("_MyWindowProc", "ptr", "hwnd;uint;long;ptr")
$wProcOld = _WinAPI_SetWindowLong(GUICtrlGetHandle($Input1), $GWL_WNDPROC, DllCallbackGetPtr($wProcNew))
$DummyMenu = GUICtrlCreateDummy()
$ContextMenu = GUICtrlCreateContextMenu($DummyMenu)
$CommonMenuItem = GUICtrlCreateMenuItem("这是什么?", $ContextMenu)
GUISetState(@SW_SHOW)
While 1
If _IsFocused($Form1, $Input1) And StringLen(GUICtrlRead($Input1)) > 2 Or test() Then
GUICtrlSetData($Input1, StringLeft(GUICtrlRead($Input1), StringLen(GUICtrlRead($Input1)) - 1))
EndIf
$nMsg = GUIGetMsg()
Switch $nMsg
Case - 3
Exit
EndSwitch
WEnd
Func _IsFocused($hWnd, $nCID)
Return ControlGetHandle($hWnd, '', $nCID) = ControlGetHandle($hWnd, '', ControlGetFocus($hWnd))
EndFunc ;==>_IsFocused
Func test()
If StringLen(GUICtrlRead($Input1)) = 1 Then
Return Not StringRegExp(StringRight(GUICtrlRead($Input1), 1), "^+$")
EndIf
If StringLen(GUICtrlRead($Input1)) = 2 Then
If Int(StringLeft(GUICtrlRead($Input1), 1)) > 5 Then
Return GUICtrlSetData($Input1, GUICtrlRead($Input1) & "")
EndIf
If Int(StringLeft(GUICtrlRead($Input1), 1)) = 5 Then
Return Not StringRegExp(StringRight(GUICtrlRead($Input1), 1), "^+$")
Else
Return Not StringRegExp(StringRight(GUICtrlRead($Input1), 1), "^+$")
EndIf
EndIf
EndFunc ;==>test
Func do_clever_stuff_with_clipboard($hWnd)
Local $sData
$sData = ClipGet()
If @error Then Return 0
$sData = StringUpper($sData)
GUICtrlSetData(_WinAPI_GetDlgCtrlID($hWnd), $sData)
Return 1
EndFunc ;==>do_clever_stuff_with_clipboard
Func ShowMenu($hWnd, $nContextID)
Local $iSelected = _GUICtrlMenu_TrackPopupMenu(GUICtrlGetHandle($nContextID), $hWnd, -1, -1, -1, -1, 2)
Switch $iSelected
Case $CommonMenuItem
;..........
EndSwitch
EndFunc ;==>ShowMenu
Func _MyWindowProc($hWnd, $uiMsg, $wParam, $lParam)
Switch $uiMsg
Case $WM_PASTE
Return do_clever_stuff_with_clipboard($hWnd)
Case $WM_CONTEXTMENU
If $hWnd = GUICtrlGetHandle($Input1) Then
ShowMenu($Form1, $ContextMenu)
Return 0
EndIf
Case $WM_SETCURSOR
GUICtrlSetCursor(_WinAPI_GetDlgCtrlID($hWnd), 5)
Return 1
EndSwitch
Return _WinAPI_CallWindowProc($wProcOld, $hWnd, $uiMsg, $wParam, $lParam)
EndFunc ;==>_MyWindowProc 我记得论坛之前有人发过可以禁止粘贴进去的代码,不过忘记收藏在哪里了,一时找不出来 Opt("GUIOnEventMode", 1)
$GUI = GUICreate("", 300, 180)
GUISetOnEvent(-3, "main")
GUICtrlCreateLabel("输 入:", 30, 64, 60, 17)
$Input = GUICtrlCreateInput('', 95, 60, 175, 20)
GUISetState(@SW_SHOW, $GUI)
While 1
$in = GUICtrlRead($Input)
If StringRegExp($in, "\D|^+$|^0+$|^{3,3}+$") Then GUICtrlSetData($Input,StringTrimRight($in,1))
WEnd
Func main()
Switch @GUI_CtrlId
Case - 3
Exit
EndSwitch
EndFunc ;==>main使用正则 楼上的代码精悍 东抄西拼,只能做到输入框不能用右键粘贴,不能能禁止键盘的快捷键,可以试下在当前GUI上注册CTRL + V 类的快捷 ...
ceoguang 发表于 2010-1-2 22:43 http://www.autoitx.com/images/common/back.gif
禁止ctrl+vIf ControlGetFocus ($Form1) = "Edit1" Then ClipPut ("") 本帖最后由 pusofalse 于 2010-1-3 00:41 编辑
Re 9#:
粘贴对应的消息是WM_PASTE。
Re 11#:
学习了,资源占用方面有些问题,我这里运行后,CPU会飙到50%。
Re 13#:
ClipPut("")只是清空剪切板,而非真正意义上的阻止粘贴操作。
感谢3位的参与,3位的代码都有共同之处,都不是直接阻止非法字符的输入的,而是在输入之后,已经显示出来之时,再加以判断的。都会存在极细微的闪屏现象,用循环读取内容的方法绝非上策,也会存在这样的闪屏和效率、资源占用问题。解决方法是,在字符还没有显示出来之前(消息还没有传递到消息链终点之前),就应该得到input框中的内容将会是怎样的。
用户在框中输入字符时,发送的消息是WM_CHAR,此时字符还不会显示出来。 2#的就是楼主发的!
楼主来帮忙解释下If StringRegExp(ClipGet(), "[\x20-\x7f]") Then Return False这里的[\x20-\x7f]是什么意思?