0

Is there any way we can override the behavior of the original KeyPress event of a TextBox without adding a UserControl?

My problem is this:
I have a big project where I have over 1000 TextBoxes. Those reside in different Forms. I have to validate user input to prevent entering special characters.

I know the code to prevent that, but then I have to put that code in every KeyPress event handler of each TextBox, or I have to make a Custom Control derived from TextBox class, override OnKeypress, call the base class in case if there are explicitly codes declared in KeyPress events on Forms and replace the Custom Control everywhere.

Is there any other method to intercept the KeyPress events of a TextBox and make this change in one place only?

Jimi
  • 29,621
  • 8
  • 43
  • 61
Prageeth Liyanage
  • 1,612
  • 2
  • 19
  • 41
  • 1
    Does this answer your question? [To prevent entering special characters on textbox ( Already have thousand of textboxes in solution )](https://stackoverflow.com/questions/64238735/to-prevent-entering-special-characters-on-textbox-already-have-thousand-of-tex) – IndieGameDev Oct 07 '20 at 09:28
  • @MathewHD Unfortunately no – Prageeth Liyanage Oct 07 '20 at 09:29
  • What do you exactly want to achieve? If i understood it correctly you just want all your textboxes to use the same KeyDown Event and I've added an answer on how to do that on your original question. – IndieGameDev Oct 07 '20 at 09:29
  • @MathewHD To intercept with current keypress events of already implemented textboxes. Run if there is any code inside keypress events of those, Then run my validation code finally. Without adding any user control. – Prageeth Liyanage Oct 07 '20 at 09:31
  • Sure. You can add a class object to the Project where you add the event handler and make your Controls subscribe to the event using this handler only. I suggest to make a Custom Control (not UserControl, different thing) (btw, in a Custom Control, you don't *override the event*, you override the method that raises the event - `OnKeyPress`, in this case - and of course call `base.OnKeyPress(e)` to allow the normal use of the event). Then you have a reusable Control that contains the logic required, you can expand it with new features if needed. No need to change anything else in the Project. – Jimi Oct 07 '20 at 10:29
  • @Jimi With all due respect can you please help me regarding this? Any sample. Please. – Prageeth Liyanage Oct 07 '20 at 14:13

2 Answers2

2

A couple of examples:

  1. A simple shared class that allows to subscribe to events using predefined event handlers.
  2. A Custom Control that can filter key presses based on specific allowed patterns. Here, I'm using what you posted earlier in relation to the KeyPress event filters. This filter may need some more attention.
    I've added, as an example, a public Property, public bool NumbersOnly, which can be used to make the custom TextBox control accept only numbers. It purges the text from any non digits chars when it's activated. An ErrorProvider gives feedback when a key that is not allowed is pressed.

Sample centralized Event Handler.

public partial class SomeForm : Form
{
    public SomeForm()
    {
        InitializeComponent();

        // Get all TextBoxes in the current Form and subscribe to some events 
        foreach (var textBox in this.FindAll<TextBox>()) {
            textBox.TextChanged += MyEventsHandler.TextBoxTextChanged_Handler;
            textBox.KeyPress += MyEventsHandler.TextBoxKeyPress_Handler;
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        foreach (var textBox in this.FindAll<TextBox>()) {
            textBox.TextChanged -= MyEventsHandler.TextBoxTextChanged_Handler;
            textBox.KeyPress -= MyEventsHandler.TextBoxKeyPress_Handler;
        }
        base.OnFormClosing(e);
    }
}

Simple events handler class:

internal static class MyEventsHandler
{
    internal static void TextBoxTextChanged_Handler(object sender, EventArgs e)
    {
        var txt = sender as TextBox;
        Console.WriteLine(txt.Text);
    }

    internal static void TextBoxKeyPress_Handler(object sender, KeyPressEventArgs e)
    {
        if (Regex.Match(e.KeyChar.ToString(), @"[^a-zA-Z0-9\s]").Success) {
            e.Handled = true;
        }
    }
}

Extension method:

public static class ControlExtensions
{
    public static IEnumerable<T> FindAll<T>(this Control control) where T: Control
    {
        foreach (Control ctl in control.Controls) {
            foreach (Control child in ctl.FindAll<T>()) {
                if (child is T ch) yield return ch;
            }
            if (ctl is T c) yield return c;
        }
    }
}

Specialized Custom Control, inheriting TextBox

IMO, it's better to have all the filtering logic in one single Control that can be further expanded when needed

using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using System.Windows.Forms;

[ToolboxItem(true)]
[DesignerCategory("Code")]
public class TextBoxEx : TextBox
{
    private const int ES_NUMBER = 0x2000;
    private bool m_NumbersOnly = false;
    private Regex regex = new Regex(@"[^a-zA-Z0-9\s\b]", RegexOptions.Compiled);

    public TextBoxEx() { }

    public bool NumbersOnly {
        get => m_NumbersOnly;
        set {
            if (m_NumbersOnly != value) {
                m_NumbersOnly = value;
                this.Text = Regex.Replace(this.Text, @"\D", "");
                this.RecreateHandle();
            }
        }
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        if (regex.IsMatch(e.KeyChar.ToString())) {
            e.Handled = true;
        }
        base.OnKeyPress(e);
    }

    protected override CreateParams CreateParams
    {
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        get {
            CreateParams cp = base.CreateParams;
            if (m_NumbersOnly) {
                cp.Style |= ES_NUMBER;
            }
            else {
                cp.Style &= ~ES_NUMBER;
            }
            return cp;
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
-1

Handle the keypress event of the form instead, it'll 'intercept' it before it reaches your controls. You can check which control invoked it from the event argument that's passed.

JᴀʏMᴇᴇ
  • 218
  • 2
  • 15