/******************************************************************
windows 控件限制用戶的基本法門(.net 篇)
c#.net 的在下面
-------------------------------------------------------------------
本代碼演示 控制用戶的輸入的基本方式(屏蔽非數(shù)字字符輸入)
.net 下限制用戶輸入,看見很多人是在 鍵盤,或 textbox 的 textchanged 事件里做
個(gè)人認(rèn)為那樣是不正確的,
1.不能限制用戶的粘貼
2.嚴(yán)重干擾數(shù)據(jù)綁定等操作
3.有時(shí)還需要備份原始數(shù)據(jù)進(jìn)行還原
其實(shí)正確的限制輸入的時(shí)機(jī)是在,windows 消息 wm_char 觸發(fā)時(shí)
但.net 恰恰沒有提供這個(gè)消息的事件映射.怎么辦?
提供方案兩列:
1)繼承textbox 重寫 wndproc 函數(shù) (優(yōu)點(diǎn)點(diǎn)oo編程的優(yōu)點(diǎn)我不說了)
處理
if (m.msg==wm_char){
// 然后取 m.wparam 進(jìn)行判斷 m.wparam 就是用戶輸入的字符的 int 表示方式
// 如果是被限制的字符 直接 return
//不走 base.wndproc (ref m);
}
if(m.msg==wm_paste)
{
//判斷剪貼板的數(shù)據(jù)是否是符合要求如果符合不做任何處理
//否則 return 不走默然處理即可
}
base.wndproc (ref m);
2)利用api setwindowlong 替換默認(rèn)的處理消息的函數(shù)進(jìn)行處理
本文寫的就是這種 ,演示如何聲明api 而且本方法很多語(yǔ)言都可以使用,
但如果程序中有多個(gè)需要限制輸入的控件而且相做通用類庫(kù)的話
使用建議使用方案一
廢話不多說了看代碼吧.
*******************************************************************/
using system;
using system.drawing;
using system.collections;
using system.componentmodel;
using system.windows.forms;
using system.data;
using system.runtime.interopservices;
using system.text.regularexpressions;
using system.diagnostics;
namespace setwndproc
{
/// <summary>
/// form1 的摘要說明。
/// </summary>
public class form1 : system.windows.forms.form
{
//聲明一個(gè)委托
public delegate intptr newwndproc(intptr hwnd, int msg, intptr wparam, intptr lparam);
//api 具體幫助請(qǐng)察看 msdn 或到 ms 網(wǎng)站上去找
[dllimport("user32.dll", charset=charset.auto)]
public static extern intptr setwindowlong(intptr hwnd, int nindex, newwndproc wndproc);
[dllimport("user32.dll", charset=charset.auto)]
public static extern intptr setwindowlong(intptr hwnd, int nindex, intptr dwnewlong);
//沒用到
[dllimport("user32.dll", charset=charset.auto)]
public static extern intptr getwindowlong(intptr hwnd, int nindex);
[dllimport("user32.dll", charset=charset.auto)]
public static extern intptr callwindowproc(intptr wndproc, intptr hwnd, int msg, intptr wparam, intptr lparam);
//setwindowlong 用的常數(shù),不知道什么意識(shí)的去看 msdn吧
public const int gwl_wndproc = -4;
//右鍵菜單消息
public const int wm_contextmenu = 0x007b;
//粘貼消息
public const int wm_paste = 0x0302;
//輸入字符消息(鍵盤輸入的,輸入法輸入的好像不是這個(gè)消息)
public const int wm_char = 0x0102;
//一定要聲明為實(shí)列變量否則,局部變量發(fā)送給api后很容易被_u71 ?c 回收,
//會(huì)出現(xiàn)根本無(wú)法捕獲的異常
private newwndproc wpr=null;
//備份的默然處理函數(shù)
private intptr oldwndproc=intptr.zero;
private system.windows.forms.textbox textbox1;
/// <summary>
/// 必需的設(shè)計(jì)器變量。
/// </summary>
private system.componentmodel.container components = null;
public form1()
{
//
// windows 窗體設(shè)計(jì)器支持所必需的
//
initializecomponent();
//
// todo: 在 initializecomponent_u-29693 ?用后添加任何構(gòu)造函數(shù)代碼
//
}
/// <summary>
/// 清理所有正在使用的資源。
/// </summary>
protected override void dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.dispose();
}
}
base.dispose( disposing );
}
#region windows 窗體設(shè)計(jì)器生成的代碼
/// <summary>
/// 設(shè)計(jì)器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內(nèi)容。
/// </summary>
private void initializecomponent()
{
this.textbox1 = new system.windows.forms.textbox();
this.suspendlayout();
//
// textbox1
//
this.textbox1.location = new system.drawing.point(32, 16);
this.textbox1.name = "textbox1";
this.textbox1.tabindex = 0;
this.textbox1.text = "555";
this.textbox1.textalign = system.windows.forms.horizontalalignment.right;
//
// form1
//
this.autoscalebasesize = new system.drawing.size(6, 14);
this.clientsize = new system.drawing.size(152, 53);
this.controls.add(this.textbox1);
this.name = "form1";
this.text = "form1";
this.load += new system.eventhandler(this.form1_load);
this.closed += new system.eventhandler(this.form1_closed);
this.resumelayout(false);
}
#endregion
/// <summary>
/// 應(yīng)用程序的主入口點(diǎn)。
/// </summary>
[stathread]
static void main()
{
application.run(new form1());
}
private intptr textboxwndproc(intptr_u104 ?wnd, int msg, intptr wparam, intptr lparam)
{
intptr returnvar=intptr.zero;
switch (msg)
{
//粘貼消息包括 ctrl+v or 右鍵菜單粘貼
case wm_paste:
//取剪貼板對(duì)象
idataobject idata = clipboard.getdataobject();
//判斷是否是text
if(idata.getdatapresent(dataformats.text))
{
//取數(shù)據(jù)
string str;
str = (string)idata.getdata(dataformats.text);
/*
如果需要正負(fù)號(hào),先要判斷textbox 上光標(biāo)的位置
如果光標(biāo)在最前面可以用這個(gè),^(((/+|-)/d)?/d*)$
下面的 wm_char 也要做相應(yīng)變化
*/
//如果是數(shù)字(可以粘貼跳出)
if (regex.ismatch(str,@"^(/d{1,})$")) break;
}
//不可以粘貼
return (intptr)0;
case wm_char:
int keychar=wparam.toint32();
debug.writeline(keychar);
bool charok=(keychar>47 && keychar<58) || //數(shù)字
keychar==8 || //退格
keychar==3 || keychar==22 || keychar==24;//拷貝,粘貼,剪切
//如果不是需要的的字符 wparam 改為字符 0
//return (intptr)0; 也行不過沒有禁止輸入的 鍵盤音
if (!charok) wparam=(intptr)0;
break;
//禁止右鍵菜單(如果需要的話)
//case wm_contextmenu:
//return (intptr)0;
}
//回調(diào)備份的默認(rèn)處理的函數(shù)
returnvar= callwindowproc(oldwndproc,hwnd,msg,wparam,lparam);
return returnvar;
}
private void form1_load(object sender, system.eventargs e)
{
this.show();
//備份默認(rèn)處理函數(shù)
//oldwndproc=getwindowlong(textbox1.handle,gwl_wndproc);
//實(shí)列化委托(這里就是回調(diào)函數(shù))
wpr= new newwndproc(this.textboxwndproc);
//替換控件的默認(rèn)處理函數(shù)(并且返回原始的 默認(rèn)處理函數(shù),是一個(gè)函數(shù)指針的地質(zhì))
oldwndproc=setwindowlong(textbox1.handle,gwl_wndproc,wpr);
}
private void form1_closed(object sender, system.eventargs e)
{
//還原默認(rèn)處理函數(shù)
if (!oldwndproc.equals(intptr.zero))
setwindowlong(textbox1.handle,gwl_wndproc,oldwndproc);
}
}
}