1

I have to validate a Usercontrol (textbox) from inserting special characters. In keypress event I use this code to handle this.

OnKeyPress Overwrite:

protected override void OnKeyPress(KeyPressEventArgs e) {
    base.OnKeyPress(e);
    MethodToPrevent(e);
}

MethodToPrevent Function:

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

Now this is working fine. But if user copy paste the string with special characters this is not working. How can I handle that?

Tried

protected override void OnTextChanged(EventArgs args)

But can not catch the Sender part here.

Prageeth Liyanage
  • 1,612
  • 2
  • 19
  • 41
  • 1
    Perhaps try to use another event? [TextChanged](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.textchanged)? [Paste](https://stackoverflow.com/q/3446233/1997232)? – Sinatr Oct 08 '20 at 07:06
  • "But can not catch the Sender part here." If you're attempting to use `OnTextChanged()`, then the "sender" is the class that you are in since that is a method OVERRIDE and you must be inside a derived class?... – Idle_Mind Oct 08 '20 at 15:05

2 Answers2

0

Actualy your code prevent to ctrl, but it not prevent to mouse right click and paste. Maybe you can prevent mouse right click or you can use TextChanged event.

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    base.OnKeyPress(e);
    MethodToPrevent(e);
}

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

// If the user presses the wrong key, it is already blocked with the 
//MethodToPrevent() before the TextChanged().
//it is only prevent mouse right click and paste
private void textBox1_TextChanged(object sender, EventArgs e)
{
    var regex = new Regex(@"[^a-zA-Z0-9\s]");
    if (regex.IsMatch(textBox1.Text.ToString()))
    {
        textBox1.Text = "";
    }
}
Mehmet Topçu
  • 1
  • 1
  • 16
  • 31
  • 1
    So, if you get an unwanted char, you wipe out the entire Text of the Control, in its TextChanged event handler? Sinatr, in a comment, posted a link to an answer that disables WM_PASTE. It can also be used for something different, e.g., warn User that it's paste contains chars that are not allowed. – Jimi Oct 08 '20 at 07:26
  • I just wanted to specify that the textchanged event can be used. The advanced version of this code is to find and remove the unwanted character when the textchanged event runs. – Mehmet Topçu Oct 08 '20 at 07:31
  • You, usually, try not to change the text of a Control in its TextChanged event. Or to wipe out the entire text (in the TextChanged event), if User presses the wrong key. Not sure what *the advanced version of this code* means. – Jimi Oct 08 '20 at 07:34
  • No, If the user presses the wrong key, it is already blocked with the MethodToPrevent() before the TextChanged(). – Mehmet Topçu Oct 08 '20 at 07:39
  • The idea, in this kind of operations, is to prevent stuff from happening, not to try and patch it after it has already happened. So, what you want to do, is to prevent the Text from changing in the first place if something not allowed in entered, in any way. + You suppose that the KeyPress event is raised for each char that the Regex is meant to intercept. Also, as mentioned, you should try to avoid, in principle, to change the Text of a Control in its TextChanged event (yes, I know, I've already said that). – Jimi Oct 08 '20 at 08:14
  • @MehmetTopçu there is no textBox1_TextChanged event in a user control. Only protected override void OnTextChanged(EventArgs args) event – Prageeth Liyanage Oct 08 '20 at 08:37
  • @Jimi Do you have any suggestion to prevent the unwanted characters from entering on copy paste event? – Prageeth Liyanage Oct 08 '20 at 08:38
  • @Prageeth Liyanage As mentioned in the first comment, the code that Sinatr linked, related to `WM_PASTE`, can (and should) be used to intercept a Paste event **before** the text is transferred to the Control. There, you can evaluate whether the Text pasted in contains disallowed chars, then either remove the *bad* chars or prevent the Text from reaching the TextBox entirely (you just don't call `base.WndProc(ref m)`). The example there raises an event that can be use to notify the user of the choice made, if necessary (or do anything else is needed when a Paste event occurs in this context). – Jimi Oct 08 '20 at 08:48
  • @Prageeth Liyanage BTW, in a Custom Control, `OnTextChanged()` is the method that raises the `TextChanged` event (calling `base.OnTextChanged(e)`): it's actually more or less the same thing, just one step ahead. If you don't call `base` in the body of the method, the event is not raised at all. – Jimi Oct 08 '20 at 08:54
0

Since you're apparently using a Custom Control (TextBox derived), some suggestions to handle the User Paste actions and filtering text edits in your Control (the filter part is what you provided, more work is required in this department to make this bullet-proof - not just in relation to error handling).

This Custom Control adds some features to the standard TextBox:

  1. Filters chars handled by OnKeyPress, allowing cursor movements, Delete an BackSpace (I've added \b to the filter Regex, I think it was missing).

  2. Filters WM_PASTE events in 3 different ways, using the PasteAction Enum, linked to the Public UserPaste Property, which defines the Control behavior in reaction to a Paste operation (modify as required):

    • Allow: a User can paste anything inside the TextBox
    • Partial: a User can paste only what the Regex filter allows, the rest is removed
    • Disallow: a User cannot paste anything
  3. Has an option to allow Numbers only, at the base class level (no Regex). This is coupled by the feedback provided by the ErrorProvider class.

To allow the partial paste feature, the WndProc override intercepts WM_PASTE, filters the Text read from the Clipboard, using the Clipboard.GetText() method (with TextDataFormat.UnicodeText), then sends a EM_REPLACESEL message to the edit control, to add the modified Text (to the User, it appears as an actual paste operation).

base.WndProc() is not called in any case and the Clipboard is untouched.
→ If you mean to notify a User of the action taken, please don't show a MessageBox in the WndProc method override.

Note: this is a modified version of the Custom Control I've already posted here (with some other methods that may come in handy), for the same exact matter. → If the two questions are actually related, let me know.


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

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

    public TextBoxEx() { }

    public enum PasteAction
    {
        Allow,
        Disallow,
        Partial
    }

    public PasteAction UserPaste { get; set; }

    public override string Text {
        get => base.Text;
        set {
            if (!base.Text.Equals(value)) {
                base.Text = regex.Replace(value, "");
            }
        }
    }

    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 |= NativeMethods.ES_NUMBER;
            }
            else {
                cp.Style &= ~NativeMethods.ES_NUMBER;
            }
            return cp;
        }
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg) {
            case NativeMethods.WM_PASTE:
                switch (UserPaste) {
                    case PasteAction.Disallow:
                        return;
                    case PasteAction.Partial:
                        string text = Clipboard.GetText(TextDataFormat.UnicodeText);
                        text = regex.Replace(text, "");
                        NativeMethods.SendMessage(this.Handle, NativeMethods.EM_REPLACESEL, 1, text);
                        return;
                    case PasteAction.Allow:
                        break;
                }
                break;
        }
        base.WndProc(ref m);

    }

    private class NativeMethods
    {
        internal const int WM_PASTE = 0x0302;
        internal const int ES_NUMBER = 0x2000;
        internal const int EM_REPLACESEL = 0xC2;

        [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        internal static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, string lParam);
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61