Windows 控件限制用户输入的基本法门(.NET 篇) ===========转贴

来源:互联网 发布:excel怎么合并两列数据 编辑:程序博客网 时间:2024/05/17 08:46

/******************************************************************

 

                         Windows 控件限制用户的基本法门(.NET )

                                        VB.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/2004/10/31/161024.aspx

'

'-------------------------------------------------------------------

'    本代码演示 控制用户的输入的基本方式(屏蔽非数字字符输入)

'    .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



版权声明:CSDN是本Blog托管服务提供商。如本文牵涉版权问题,CSDN不承担相关责任,请版权拥有者直接与文章作者联系解决。

发表于 2004年10月31日 4:18 PM

评论

# 回复:Windows 控件限制用户输入的基本法门(.NET 篇) 2004-10-31 6:07 PM flashelf
如转载请通知本人

# 回复:Windows 控件限制用户输入的基本法门(.NET 篇) 2004-11-01 10:35 AM hbxtlhx
看看先,试试先,呵呵

# 回复:Windows 控件限制用户输入的基本法门(.NET 篇) 2004-11-01 10:50 AM 海宁
private IntPtr TextBoxWndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
不错,上一句你的是乱码

# 回复:Windows 控件限制用户输入的基本法门(.NET 篇) 2004-11-01 10:59 AM AhBian
不错,我测试一下.
原贴:http://blog.csdn.net/FlashElf/archive/2004/10/31/161024.aspx
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 自己做的葡萄酒太甜怎么办 吉米学校想退款怎么办 汽车租赁公司不退押金怎么办 身材不好怎么办健身教练形象照 反祈祷式做不到怎么办 窦性心跳过缓怎么办 四维彩超查出胎儿心脏有问题怎么办 减脂肚子饿了怎么办 做瑜伽腰扭到了怎么办 出了汗怎么办活动反思 农村都是老人和孩子怎么办 瑜伽垫容易出现痕迹怎么办 37岁失业了该怎么办 45岁找什么工作怎么办 华为手机4g网速慢怎么办 解析软件包时出现问题怎么办 一字马不能下去髋摆不正怎么办 练轮瑜伽骆驼式腰疼怎么办 感昌咳嗽老不好怎么办 我感昌一直不好怎么办 腰间盘突出晚上睡觉痛怎么办 天天吃撑了怎么办啊 一吸气就想咳嗽怎么办 鼻子堵住了怎么办没法吸气时 一只鼻子不通气怎么办 练瑜伽后特别饿怎么办 站一天小腿肿了怎么办 练腹肌腰粗了怎么办 大专不交学费.然后退学怎么办 练瑜伽压腿一字马受伤了怎么办 银行工作人员借钱不还怎么办 借钱不还跑了但有工作怎么办 亲戚家借钱不还怎么办 学习瑜伽教练口令好复杂怎么办 练瑜伽腿的柔韧性不够怎么办 瑜伽扭转时手抓不到脚怎么办 练瑜伽腿部太硬怎么办 褶皱衣服不紧了怎么办 吃撑了肚子涨怎么办 正好压本科线该怎么办 大脚趾被砸了怎么办