Windows 控件限制用户输入的基本法门(.NET 篇) 选择自 FlashElf 的 Blog

类别:.NET开发 点击:0 评论:0 推荐:
 

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

 

                         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


本文地址:http://com.8s8s.com/it/it43107.htm