问题描述
我们有一个使用c#(Windows窗体)编写的高级软件。在它们中,我们有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)派生的自定义控件,其中包含我进行验证所需的所有逻辑,因此所有操作都在一个地方完成。 但是我仍然必须再次将所有现有的文本框更改为新的文本框。 有什么办法可以改变当前事件处理程序的行为?
注意:我需要重写文本框的当前按键事件并运行我的验证代码,并且如果按键事件中有明确提及的代码,则还需要运行。
解决方法
如果要将KeyDown
事件添加到所有TextBoxes
中,则可以遍历它们并为所有它们添加相同的EventHandler
。
首先,我们需要创建一个遍历所有TextBox的函数。
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分配给所有TextBox。
调用函数:
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
}
,
“有没有办法从一个地方处理这个问题并影响 每个文本框”
有几种方法。但是似乎您不想编辑文本框本身,所以我知道只有一种可靠的方法;连接全局键盘挂钩。代码如下:
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,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,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()之前解决此问题。