/******************************************************************
Windows 控件限制用户的基本法门(.NET 篇)
-------------------------------------------------------------------
本代码演示 控制用户的输入的基本方式(屏蔽非数字字符输入)
.net 下限制用户输入,看见很多人是在 键盘,或 textBox 的 TextChanged 事件里做
个人认为那样是不正确的,
1.不能限制用户的粘贴
2.严重干扰数据绑定等操作
3.有时还需要备份原始数据进行还原
其实正确的限制输入的时机是在,windows 消息 WM_CHAR 触发时
但.net 恰恰没有提供这个消息的事件映射.怎么办?
提供方案两列:
1)继承TextBox 重写 WndProc 函数 (优点点oo编程的优点我不说了)
处理
if (m.Msg==WM_CHAR){
// 然后取 m.WParam 进行判断 m.WParam 就是用户输入的字符的 int 表示方式
// 如果是被限制的字符 直接 Return
//不走 base.WndProc (ref m);
}
if(m.Msg==WM_PASTE)
{
//判断剪贴板的数据是否是符合要求如果符合不做任何处理
//否则 Return 不走默然处理即可
}
base.WndProc (ref m);
2)利用API SetWindowLong 替换默认的处理消息的函数进行处理
本文写的就是这种 ,演示如何声明API 而且本方法很多语言都可以使用,
但如果程序中有多个需要限制输入的控件而且相做通用类库的话
使用建议使用方案一
废话不多说了看代码吧.
*******************************************************************/
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
{
//声明一个委托
public delegate IntPtr NewWndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
//API 具体帮助请察看 MSDN 或到 MS 网站上去找
[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 用的常数,不知道什么意识的去看 msdn吧
public const int GWL_WNDPROC = -4;
//右键菜单消息
public const int WM_CONTEXTMENU = 0x007b;
//粘贴消息
public const int WM_PASTE = 0x0302;
//输入字符消息(键盘输入的,输入法输入的好像不是这个消息)
public const int WM_CHAR = 0x0102;
//一定要声明为实列变量否则,局部变量发送给API后很容易被_u71 ?C 回收,
//会出现根本无法捕获的异常
private NewWndProc wpr=null;
//备份的默然处理函数
private IntPtr oldWndProc=IntPtr.Zero;
private System.Windows.Forms.TextBox textBox1;
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent_u-29693 ?用后添加任何构造函数代码
//
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </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>
/// 应用程序的主入口点。
/// </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:
//取剪贴板对象
IDataObject iData = Clipboard.GetDataObject();
//判断是否是Text
if(iData.GetDataPresent(DataFormats.Text))
{
//取数据
string str;
str = (String)iData.GetData(DataFormats.Text);
/*
如果需要正负号,先要判断TextBox 上光标的位置
如果光标在最前面可以用这个,^(((\+|-)\d)?\d*)$
下面的 WM_CHAR 也要做相应变化
*/
//如果是数字(可以粘贴跳出)
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) || //数字
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;
}
//回调备份的默认处理的函数
returnVar= CallWindowProc(oldWndProc,hWnd,msg,wParam,lParam);
return returnVar;
}
private void Form1_Load(object sender, System.EventArgs e)
{
this.Show();
//备份默认处理函数
//oldWndProc=GetWindowLong(textBox1.Handle,GWL_WNDPROC);
//实列化委托(这里就是回调函数)
wpr= new NewWndProc(this.TextBoxWndProc);
//替换控件的默认处理函数(并且返回原始的 默认处理函数,是一个函数指针的地质)
oldWndProc=SetWindowLong(textBox1.Handle,GWL_WNDPROC,wpr);
}
private void Form1_Closed(object sender, System.EventArgs e)
{
//还原默认处理函数
if (!oldWndProc.Equals(IntPtr.Zero))
SetWindowLong(textBox1.Handle,GWL_WNDPROC,oldWndProc);
}
}
}
FlashElf 2004年10月31日16:25:21
'/******************************************************************
' Windows 控件限制用户输入的基本法门(.NET 篇) For VB.NET
' C# 篇
' http://blog.csdn.net/flashelf/archive/
'
'-------------------------------------------------------------------
' 本代码演示 控制用户的输入的基本方式(屏蔽非数字字符输入)
' .net 下限制用户输入,看见很多人是在 键盘,或 textBox 的 TextChanged 事件里做
' 个人认为那样是不正确的,
' 1.不能限制用户的粘贴
' 2.严重干扰数据绑定等操作
' 3.有时还需要备份原始数据进行还原
' 其实正确的限制输入的时机是在,windows 消息 WM_CHAR 触发时
' 但.net 恰恰没有提供这个消息的事件映射.怎么办?
' 提供方案两列:
' 1)继承TextBox 重写 WndProc 函数 (优点点oo编程的优点我不说了)
' 处理
' if (m.Msg==WM_CHAR){
' // 然后取 m.WParam 进行判断 m.WParam 就是用户输入的字符的 int 表示方式
' // 如果是被限制的字符 直接 Return
' //不走 base.WndProc (ref m);
' }
' if(m.Msg==WM_PASTE)
' {
' //判断剪贴板的数据是否是符合要求如果符合不做任何处理
' //否则 Return 不走默然处理即可
' }
' base.WndProc (ref m);
' 2)利用API SetWindowLong 替换默认的处理消息的函数进行处理
' 本文写的就是这种 ,演示如何声明API 而且本方法很多语言都可以使用,
' 但如果程序中有多个需要限制输入的控件而且相做通用类库的话
' 使用建议使用方案一
'废话不多说了看代码吧.
'*******************************************************************/
Imports System.Runtime.InteropServices
Imports System.Text.RegularExpressions
Public Class Form1
Inherits System.Windows.Forms.Form
'声明一个委托
Public Delegate Function NewWndProc(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
'声明 N 个API
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function CallWindowProc(ByVal wndProc As IntPtr, ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal wndproc As NewWndProc) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As IntPtr) As IntPtr
End Function
'要用的常数
Public Const GWL_WNDPROC As Integer = -4
'输入字符消息(键盘输入的,输入法输入的好像不是这个消息)
Public Const WM_CHAR As Integer = 258
'右键菜单消息
Public Const WM_CONTEXTMENU As Integer = 123
'粘贴消息
Public Const WM_PASTE As Integer = 770
Private wpr As NewWndProc
Private oldWndProc As IntPtr
#Region " Windows 窗体设计器生成的代码 "
Public Sub New()
MyBase.New()
'该调用是 Windows 窗体设计器所必需的。
InitializeComponent()
'在 InitializeComponent() 调用之后添加任何初始化
End Sub
'窗体重写 dispose 以清理组件列表。
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Windows 窗体设计器所必需的
Private components As System.ComponentModel.IContainer
'注意: 以下过程是 Windows 窗体设计器所必需的
'可以使用 Windows 窗体设计器修改此过程。
'不要使用代码编辑器修改它。
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'TextBox1
'
Me.TextBox1.Location = New System.Drawing.Point(24, 16)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = ""
Me.TextBox1.TextAlign = System.Windows.Forms.HorizontalAlignment.Right
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
Me.ClientSize = New System.Drawing.Size(160, 53)
Me.Controls.Add(Me.TextBox1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region
Private Function TextBoxWndProc(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
Dim returnVar As IntPtr = IntPtr.Zero
Select Case msg
Case WM_CHAR
Dim keyChar As Integer = wParam.ToInt32()
Dim charOk As Boolean = (keyChar > 47 AndAlso keyChar < 58) _
OrElse keyChar = 8 OrElse _
keyChar = 3 OrElse keyChar = 22 OrElse keyChar = 24
'如果不是需要的的字符 wParam 改为字符 0
'return (IntPtr)0; 也行不过没有禁止输入的键盘音
If Not charOk Then
wParam = IntPtr.Zero
End If
Exit Select
'粘贴消息包括 Ctrl+V Or 右键菜单粘贴
Case WM_PASTE
'取剪贴板对象
Dim obj1 As IDataObject = Clipboard.GetDataObject
'判断是否是Text
If obj1.GetDataPresent(DataFormats.Text) Then
Dim text1 As String = CType(obj1.GetData(DataFormats.Text), String)
'如果需要正负号,先要判断TextBox 上光标的位置
'如果光标在最前面可以用这个,^(((\+|-)\d)?\d*)$
'下面的 WM_CHAR 也要做相应变化
'
'如果是数字(可以粘贴跳出)
If Regex.IsMatch(text1, "^(\d{1,})$") Then
Exit Select
End If
End If
Return IntPtr.Zero
End Select
returnVar = CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam)
Return returnVar
End Function
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'//实列化委托(这里就是回调函数)
wpr = New NewWndProc(AddressOf Me.TextBoxWndProc)
'//替换控件的默认处理函数(并且返回原始的 默认处理函数,是一个函数指针的地质)
oldWndProc = SetWindowLong(TextBox1.Handle, GWL_WNDPROC, wpr)
End Sub
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
'还原默认处理函数
If Not oldWndProc.Equals(IntPtr.Zero) Then
SetWindowLong(TextBox1.Handle, GWL_WNDPROC, oldWndProc)
End If
End Sub
End Class
Flashelf 2004年10月31日18:00:19
本文地址:http://com.8s8s.com/it/it43107.htm