2

I want to filter the data that user enters. I just want him to be able to add numbers and , or . and to delete in case of mistake.

I have this code:

private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if ((char.IsDigit(e.KeyChar) == false) && 
        (e.KeyChar != '\b') && (e.KeyChar != ',')) 
            e.Handled = true;
    if (e.KeyChar == ',' && (sender as TextBox).Text.IndexOf(',') > 0) 
        e.Handled = true;
}

Now I have two problems:
1) I have two textBoxes (which are fields where user should write some numbers which will be added/divided etc.) and when I write only a comma in one of them there is an error.
2) I also want to make it possible to use ".", but I want it to change into ",".

Flat Eric
  • 7,971
  • 9
  • 36
  • 45
Jeling
  • 35
  • 1
  • 1
  • 7

3 Answers3

1

Here's the code I wrote, using WndProc to get as priority as possible to intercept events, and the code below handles, adding to the key press, the paste and setting text programmatically.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace NetTools.GUI
{

    public class FastTextBox : TextBox
    {

        [DebuggerHidden, DebuggerNonUserCode, DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string hint = string.Empty;

        /// <summary>
        /// Is called in preview the system handle the changes, and determines whether the text will be accepted and handled by the system.
        /// </summary>
        /// <param name="oldText">The old text.</param>
        /// <param name="newText">The current text (included the newer changes).</param>
        /// <param name="input">The newer changes.</param>
        /// <param name="offset">Start position of changes.</param>
        /// <param name="length">Length of <paramref name="input"/>.</param>
        /// <returns>Whether the system can handle the text.</returns>
        public delegate bool TextAcceptorEventHandler(string oldText, string newText, string input, int offset, int length);

        /// <summary>
        /// Is called in preview of or after the system handle the changes.
        /// </summary>
        /// <param name="oldText">The old text.</param>
        /// <param name="newText">The current text (included the newer changes).</param>
        /// <param name="input">The newer changes.</param>
        /// <param name="offset">Start position of changes.</param>
        /// <param name="length">Length of <paramref name="input"/>.</param>
        public delegate void TextEventHandler(string oldText, string newText, string input, int offset, int length);

        /// <summary>
        /// Is called in preview of the system handle the changes.
        /// </summary>
        public event TextEventHandler PreviewTextChange = null;

        /// <summary>
        /// Is called after the system handled the changes.
        /// </summary>
        public event TextEventHandler AfterTextChange = null;

        public string Hint
        {
            [DebuggerHidden]
            get
            {
                return hint;
            }
            [DebuggerHidden]
            set
            {
                if (value == hint)
                    return;
                hint = value;
                SendMessage(Handle, 0x1501, 1, value);
            }
        }

        public TextAcceptorEventHandler TextAcceptor
        {
            [DebuggerHidden]
            get;
            [DebuggerHidden]
            set;
        }

        [DebuggerHidden]
        protected override void CreateHandle()
        {
            base.CreateHandle();
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.FixedWidth | ControlStyles.FixedHeight | ControlStyles.Opaque, DoubleBuffered = true);
        }

        /// <summary>
        /// Is called in preview of a text change, before the system handles it, and determines whether the text will be accepted and handled by system.
        /// </summary>
        /// <param name="oldText">The last written text.</param>
        /// <param name="newText">The current text (included the newer changes).</param>
        /// <param name="input">The newer changes.</param>
        /// <param name="offset">Start position of changes.</param>
        /// <param name="length">Length of <paramref name="input"/>.</param>
        /// <returns></returns>
        [DebuggerHidden]
        protected virtual bool OnAcceptTextInput(string oldText, string newText, string input, int offset, int length)
        {
            return TextAcceptor == null ? true : TextAcceptor.Invoke(oldText, newText, input, offset, length);
        }

        /// <summary>
        /// Is called in preview of a text change, before the system handles it.
        /// </summary>
        /// <param name="oldText">The last written text.</param>
        /// <param name="newText">The current text (included the newer changes).</param>
        /// <param name="input">The newer changes.</param>
        /// <param name="offset">Start position of changes.</param>
        /// <param name="length">Length of <paramref name="input"/>.</param>
        [DebuggerHidden]
        protected virtual void OnPreviewTextChange(string oldText, string newText, string input, int offset, int length)
        {
            if (PreviewTextChange != null)
                PreviewTextChange.Invoke(oldText, newText, input, offset, length);
        }

        /// <summary>
        /// Is called after the system handled the changes.
        /// </summary>
        /// <param name="oldText">The old text.</param>
        /// <param name="newText">The current text (included the newer changes).</param>
        /// <param name="input">The newer changes.</param>
        /// <param name="offset">Start position of changes.</param>
        /// <param name="length">Length of <paramref name="input"/>.</param>
        [DebuggerHidden]
        protected virtual void OnAfterTextChange(string oldText, string newText, string input, int offset, int length)
        {
            if (AfterTextChange != null)
                AfterTextChange.Invoke(oldText, newText, input, offset, length);
        }

        [DebuggerHidden]
        private bool CallbackPreview(string oldText, string newText, string input, int offset, int length)
        {
            if (string.IsNullOrEmpty(newText) ? true : OnAcceptTextInput(oldText, newText, input, offset, length))
            {
                OnPreviewTextChange(oldText, newText, input, offset, length);
                return true;
            }
            return false;
        }

        [DebuggerHidden]
        private bool CallbackPreview(ref string newText, string oldText, string input, int offset, int length)
        {
            newText = GetNewText(oldText, input, offset, length);
            return CallbackPreview(oldText, newText, input, offset, length);
        }

        [DebuggerHidden]
        private string GetNewText(string oldText, string input, int offset, int length)
        {
            if (input.Length == 1 ? input.ToCharArray()[0] == (int)Keys.Back : false)
                return Text.Length > 0 ? $"{oldText.Substring(0, offset - (length == 0 ? 1 : 0))}{oldText.Substring(offset + length)}" : string.Empty;
            else
                return $"{(offset > 0 ? oldText.Substring(0, offset) : string.Empty)}{input}{(offset == oldText.Length - 1 ? string.Empty : oldText.Substring(offset + length))}";
        }

        [DebuggerHidden]
        protected override void WndProc(ref Message m)
        {
            string text = "";
            switch (m.Msg)
            {
                case 0x102:
                    int code = m.WParam.ToInt32();
                    if (code == 22 && ModifierKeys.HasFlag(Keys.Control))
                        goto case 0x302;
                    text = ((char)code).ToString();
                    goto case 0xC;
                case 0x302:
                    text = Clipboard.GetText();
                    goto case 0xC;
                case 0xC:
                    string input = string.Empty, newText = string.Empty;
                    if (string.IsNullOrEmpty(text))
                    {
                        newText = Marshal.PtrToStringUni(m.LParam);
                        if (CallbackPreview(Text, newText, string.Empty, SelectionStart, SelectionLength))
                            base.WndProc(ref m);
                    } else
                    {
                        input = text;
                        if (CallbackPreview(ref newText, Text, input, SelectionStart, SelectionLength))
                            base.WndProc(ref m);
                    }
                    OnAfterTextChange(Text, newText, input, SelectionStart, SelectionLength);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
    }
}

Then you can just use FastTextBox as control, in place of TextBox, and set programmatically the TextAcceptor property to a delegate:

yourFastTextBox.TextAcceptor = yourTextAcceptorDelegate;

private bool CidrTextAcceptor(string oldText, string newText, string input, int offset, int length)
{
    return whether the text can be wrote or not.
}

For other explanations, see the documentation I wrote in the class.

EDIT: I improved the code based on a bug with the clipboard.

Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
0
  1. Multiple controls can reference the same event handler, so the number of textboxes is irrelevant.
  2. Replacing a character can be done by simply assigning the desired value to e.KeyChar

This code allows commas without exception:

private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if (!char.IsDigit(e.KeyChar) && e.KeyChar != ',' && e.KeyChar != '.')
    {
        e.Handled = true;
    }
    else if (e.KeyChar == '.')
    {
        e.KeyChar = ',';
    }
}

However, looking at your code, it appears that you want to allow only a single comma. This code limits the contents to containing only a single comma:

private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if (!char.IsDigit(e.KeyChar) && e.KeyChar != ',' && e.KeyChar != '.')
    {
        e.Handled = true;
    }
    else if (e.KeyChar == '.')
    {
        e.KeyChar = ',';
    }
    if (e.KeyChar == ',')
    {
        //can use .Contains, indexOf, etc.  I used count incase you want more than 1
        if(((TextBox) sender).Text.Count(s=>s==',') == 1)
        {
            e.Handled = true;
        }
    }
}

Apparently, I don't know how to format code in comments, so I'm updating the main answer to accommodate the additional requirement. This code will allow - but places it at the beginning of the number regardless of when it is pressed:

if (!char.IsDigit(e.KeyChar) && e.KeyChar != ',' && e.KeyChar != '.' && e.KeyChar != '-')
{
    e.Handled = true;
}
else if (e.KeyChar == '.')
{
    e.KeyChar = ',';
}
else if (e.KeyChar == '-')
{
    if (((TextBox) sender).Text.Contains("-"))
    {
        e.Handled = true;
    }
    else
    {
        ((TextBox) sender).Text = "-" + ((TextBox) sender).Text;
        e.Handled = true;
    }
}
if (e.KeyChar == ',')
{
    if(((TextBox) sender).Text.Count(s=>s==',') == 1)
    {
        e.Handled = true;
    }
}
Lemiarty
  • 124
  • 10
  • Great, this helped a lot. And I have just one more thing I just thought about: "-" should be allowed only at the beginning... – Jeling May 30 '14 at 19:09
  • I updated the original "answer" post with the additional requirement because I couldn't get the `code` formatting to work in a comment. – Lemiarty May 30 '14 at 20:31
0

Have you thought about using Regular expression validator and textbox in your aspx page?

<asp:TextBox ID="txtDevice" runat="server" Text="sample"></asp:TextBox>
                <asp:RegularExpressionValidator ID="revDeviceTextBnd" Text="*" Display="Dynamic"  runat="server" ControlToValidate="txtDevice" ValidationExpression="Your regex goes here" ValidationGroup="onBnd" >
</asp:RegularExpressionValidator>

This is just another approach though.

Phoenix
  • 1,045
  • 1
  • 14
  • 22
Rex
  • 1,134
  • 15
  • 26