【问题标题】:To prevent entering special characters on textbox ( Already have thousand of textboxes in solution )防止在文本框中输入特殊字符(解决方案中已经有数千个文本框)
【发布时间】:2026-01-08 01:40:02
【问题描述】:

我们有一个使用 c# ( windows forms ) 编写的高级软件。在他们中,我们有 1000 个或更多的文本框。我需要验证所有这些文本框上的用户输入,以停止输入特殊字符和任何脚本。文本框是硬编码的。

例如: 我可以在每次按键时使用以下代码来检查用户是否输入了允许的字符。

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    var regex = new Regex(@"[^a-zA-Z0-9\s]");
    if (regex.IsMatch(e.KeyChar.ToString()))
    {
        e.Handled = true;
    }
}

但是我们必须在每个文本框按键事件上实现这一点(如果没有其他解决方案,这是最后要做的事情)。有没有办法从一个地方处理这个并影响每个文本框(在某些地方文本框也有自己的按键事件)。我需要的是一种通用方法,它将在任何文本框的每个按键事件上触发。

解决方案:创建一个从 TextBox(或 TextBoxBase)派生的自定义控件,其中包含我的验证所需的所有逻辑,因此所有这些都在一个地方完成。 但是我仍然必须再次将所有现有的文本框更改为我的新文本框。 有没有办法改变当前事件处理程序的行为?

注意:我需要的是覆盖文本框的当前按键事件并运行我的验证代码,如果按键事件中有任何明确提到的代码,则需要运行。

【问题讨论】:

  • 您是动态生成文本框还是硬编码?
  • @MathewHD 文本框是硬编码的
  • 为什么不构建一个从 TextBox 派生的自定义控件,其中包含验证所需的所有逻辑,以便在一个地方完成?无需事件处理程序,单个包中的所有验证代码...
  • @Jimi 感谢 Jimi,但我仍然必须再次将所有现有文本框更改为我的新文本框。有没有办法改变当前事件处理程序的行为?
  • 是的,当然:CTRL+H 并将所有出现的new System.Windows.Forms.TextBox() 替换为new MySpecialTextBox()(无论您分配给自​​定义类对象的名称)。 - 当然,在一个虚拟项目中进行测试以熟悉 -

标签: c# winforms textbox special-characters


【解决方案1】:

如果您想将KeyDown 事件添加到所有TextBoxes,您可以遍历它们并为所有它们添加相同的EventHandler

为此,首先我们需要创建一个循环遍历所有文本框的函数。

GetChildControls 函数:

public static IEnumerable<TControl> GetChildControls<TControl>(this Control control) 
    where TControl : Control
{
    var children = (control.Controls != null) ? 
        control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>();
    return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children);
}

我们现在可以在InitializeComponent(); 之后使用该函数将Txt_KeyDown() EventHandler 分配给所有文本框。

调用函数:

public Example() {
    InitializeComponent();
    var allTextBoxes = this.GetChildControls<TextBox>();
    foreach (TextBox tb in allTextBoxes)
    {
        tb.KeyDown += Txt_KeyDown;
    }
}

private void Txt_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) {
    // Your code here
}

【讨论】:

  • 你确定这个伪递归方法吗?看起来您正在他们的Controls 集合中搜索TControl 类型的子控件。因此,您将只返回 this 的直接 TControl 子代。
【解决方案2】:

“有没有办法从一个地方处理这个问题并影响 每个文本框”

有几种方法。但似乎您不想编辑文本框本身,所以我知道只有一种可靠的方法;附加一个全局键盘挂钩。代码如下:

class GlobalKeyboardHook
        {
            #region DLL Imports
            [DllImport("user32.dll")]
            static extern IntPtr SetWindowsHookEx(int hookEventId, keyboardProc callback, IntPtr handleInstance, uint threadId);

            [DllImport("user32.dll")]
            static extern bool UnhookWindowsHookEx(IntPtr handleInstance);

            [DllImport("user32.dll")]
            static extern int CallNextHookEx(IntPtr ignoredParameter, int hookCode, int wParam, ref KeyboardHookStruct lParam);

            [DllImport("kernel32.dll")]
            static extern IntPtr LoadLibrary(string libFileName);

            [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern IntPtr GetModuleHandle(string lpModuleName);
            #endregion DLL Imports

            #region Class Declarations
            private delegate int keyboardProc(int code, int wParam, ref KeyboardHookStruct lParam);
            private keyboardProc kbdProc;

            public struct KeyboardHookStruct
            {
                public int vkCode;
                public int scanCode;
                public int flags;
                public int time;
                public int extraInfo;
            }

            private static class KeyboardMessages
            {
                public const int WH_KEYBOARD_LL = 13;
                public const int WM_KEYDOWN = 0x100;
                public const int WM_KEYUP = 0x101;
                public const int WM_SYSKEYDOWN = 0x104;
                public const int WM_SYSKEYUP = 0x105;
            }
            
            IntPtr HookPointer = IntPtr.Zero;
            IntPtr ModuleInstance = IntPtr.Zero;

            public event KeyEventHandler KeyDown;
            public event KeyEventHandler KeyUp;

            #endregion Class Declarations

            #region Class Functions
            public GlobalKeyboardHook() {
                EnableHook(true, null);
            }

            public GlobalKeyboardHook(Process P) {
                EnableHook(true, P);
            }

            ~GlobalKeyboardHook() {
                EnableHook(false, null);
            }

            public void EnableHook(bool Enabled)
            {
                EnableHook(Enabled, null);
            }

            public void EnableHook(bool Enabled, Process P) {
                if (Enabled)
                {
                    HookPointer = SetWindowsHookEx(KeyboardMessages.WH_KEYBOARD_LL, kbdProc = HookCallback, ModuleInstance = P == null ? LoadLibrary("User32") : GetModuleHandle(P.MainModule.ModuleName), 0);
                }
                else
                {
                    UnhookWindowsHookEx(HookPointer);
                    HookPointer = IntPtr.Zero;
                    ModuleInstance = IntPtr.Zero;
                    kbdProc = null;
                }  
            }

            public int HookCallback(int code, int wParam, ref KeyboardHookStruct lParam) {
                if (code >= 0) {    
                    KeyEventArgs key = new KeyEventArgs((Keys)lParam.vkCode);
                    if ((wParam == KeyboardMessages.WM_KEYDOWN || wParam == KeyboardMessages.WM_SYSKEYDOWN) && (KeyDown != null)) {
                        KeyDown(this, key) ;
                    } else if ((wParam == KeyboardMessages.WM_KEYUP || wParam == KeyboardMessages.WM_SYSKEYUP) && (KeyUp != null)) {
                        KeyUp(this, key);
                    }
                    if (key.Handled)
                        return 1;
                }
                return CallNextHookEx(HookPointer, code, wParam, ref lParam);
            }
            #endregion Class Functions
        }

要激活,我们添加以下内容:

GlobalKeyboardHook ghk = new GlobalKeyboardHook(Process.GetCurrentProcess());
Type tbType = typeof(TextBox);
ghk.KeyDown += new KeyEventHandler(() => {
    if (typeof(this.ActiveControl) == tbType)
        RunValidation(this.ActiveControl.Text);
});

一旦有了样板钩子,添加验证就变得非常简单。没有循环意味着您不会浪费处理器时间来迭代超过一千个文本框。

请记住,这将适用于当前进程中所有 TextBox 类型的控件。如果您添加自定义 TextBox 控件或不想检查所有控件 - 在调用 RunValidation() 之前应考虑到这一点。

【讨论】: