Current page: Code snippets » C#, .NET » ValidatingTextBox-Steuerelement

ValidatingTextBox-Steuerelement

Diese aufgebohrte TextBox erweitert das Standard-Steuerelement um Funktionen zur Eingabeprüfung und erlaubt die Verwendung gängiger Hotkeys zur Textbearbeitung. Sie lässt sich so konfigurieren, dass sie nur Zahleneingaben oder erlaubte Zeichen akzeptiert oder die Eingabe mit einem regulären Ausdruck prüft.

Das Steuerelement bietet diverse Optionen, um sein Verhalten zu konfigurieren:

  • Mit der Type-Eigenschaft lässt sich die Eingabeprüfung zwischen Zahleneingaben, erlaubten Zeichen, regulärem Ausdruck und benutzerdefinierter Prüfung umschalten oder zur freien Eingabe deaktivieren. Die Eigenschaften ValidCharacters und RegularExpression geben die erlaubten Zeichen bzw. den regulären Ausdruck an.
  • Mit der Eigenschaft FinalRegularExpression lässt sich zusätzlich zur Prüfung während der Eingabe ein regulärer Ausdruck angeben, der beim Verlassen des Textfelds geprüft wird. Diese Prüfung kann mit der DeferFinalCheck-Eigenschaft deaktiviert werden, um sie zu einem geeigneten Zeitpunkt mit FinalCheck() durch Benutzercode auszuführen.
  • Die Eigenschaft RequireInput legt fest, ob das Textfeld leer bleiben darf.
  • Die Eigenschaft TrimOnLeaving legt fest, ob beim Verlassen des Textfelds Leerzeichen von den Enden des eingegebenen Textes entfernt werden sollen.
  • Die ExpectedFormatDescription-Eigenschaft enthält eine Beschreibung des erwarteten Eingabeformats und wird in einem ToolTip unter der TextBox angezeigt, wenn der Anwender eine ungültige Eingabe vorgenommen hat.
  • Die AutoHeight-Eigenschaft legt fest, ob bei mehrzeiligen Textfeldern (MultiLine) die Höhe der TextBox bei gegebener Breite automatisch an den eingegebenen Text angepasst werden soll.

Das Steuerelement erlaubt die Verwendung folgender zusätzlicher Hotkeys (Tastenkombinationen):

  • Strg+A: Gesamte Eingabe markieren
  • Strg+U: Gesamte Eingabe löschen
  • Strg+W, Strg+Backspace: Letztes Wort vor dem Cursor löschen
  • Strg+Hoch: Bei mehrzeiligen Textfeldern eine Zeile nach oben blättern
  • Strg+Runter: Bei mehrzeiligen Textfeldern eine Zeile nach unten blättern

.NET Kompatibilität: 2.0, 3.5

[o] Download (CS-Datei, 13 kB, Stand: 18. September 2009)

[o] Source code view: ValidatingTextBox.cs
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Media;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Unclassified.UI
{
    public class ValidatingTextBoxEventArgs : EventArgs
    {
        string text = "";
        bool accept = true;

        /// <summary>
        /// Gets the text that should be validated or sets a new text to be inserted into the input field.
        /// </summary>
        public string Text
        {
            get
            {
                return text;
            }
            set
            {
                text = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the new text should be accepted.
        /// </summary>
        public bool Accept
        {
            get
            {
                return accept;
            }
            set
            {
                accept = value;
            }
        }

        public ValidatingTextBoxEventArgs(string text)
        {
            this.text = text;
        }
    }

    public enum ValidatingTextBoxType
    {
        /// <summary>
        /// No restriction on the text
        /// </summary>
        Default = 0,
        /// <summary>
        /// Only numeric text is allowed (digits from 0 to 9 only)
        /// </summary>
        Numeric,
        /// <summary>
        /// The list of valid characters is regarded, if not empty
        /// </summary>
        ValidCharacters,
        /// <summary>
        /// The regular expression is regarded, if not empty
        /// </summary>
        RegularExpression,
        /// <summary>
        /// The <code>ValidateText</code> event is fired to delegate the text validation
        /// </summary>
        Custom
    }
   
    public class ValidatingTextBox : TextBox
    {
        private string prevText = "";
        private int prevSelStart = 0;
        private int prevSelLen = 0;
        private ValidatingTextBoxType type = ValidatingTextBoxType.Default;
        private string validCharacters = "";
        private string regexString = "";
        private Regex regex = null;
        private string finalRegexString = "";
        private Regex finalRegex = null;
        private bool requireInput = false;
        private bool trimOnLeaving = false;
        private ToolTip errorNoticeTooltip = null;
        private string expectedFormatDescription = "";
        private bool deferFinalCheck = false;
        private bool autoHeight = false;

        /// <summary>
        /// Validate the new text before it is applied. Only used if Type is <code>Custom</code>.
        /// </summary>
        [Category("Behavior")]
        [Description("Validate the new text before it is applied. Only used if Type is Custom.")]
        public event EventHandler<ValidatingTextBoxEventArgs> ValidateText;

        /// <summary>
        /// Validate the new text before the focus leaves.
        /// </summary>
        [Category("Behavior")]
        [Description("Validate the new text before the focus leaves.")]
        public event EventHandler<ValidatingTextBoxEventArgs> FinalValidateText;

        /// <summary>
        /// Standard type of text input to be accepted
        /// </summary>
        [Category("Behavior")]
        [Description("Standard type of text input to be accepted")]
        [DefaultValue(ValidatingTextBoxType.Default)]
        public ValidatingTextBoxType Type
        {
            get
            {
                return type;
            }
            set
            {
                type = value;
            }
        }

        /// <summary>
        /// Characters to be accepted
        /// </summary>
        [Category("Behavior")]
        [Description("Characters to be accepted")]
        [DefaultValue("")]
        public string ValidCharacters
        {
            get
            {
                return validCharacters;
            }
            set
            {
                validCharacters = value;
            }
        }

        /// <summary>
        /// Regex pattern to be accepted
        /// </summary>
        [Category("Behavior")]
        [Description("Regex to be accepted")]
        [DefaultValue("")]
        public string RegularExpression
        {
            get
            {
                return regexString;
            }
            set
            {
                if (!string.IsNullOrEmpty(value))
                    regex = new Regex(value);
                else
                    regex = null;
                regexString = value;
            }
        }

        /// <summary>
        /// Regex pattern to be matched when leaving the focus
        /// </summary>
        [Category("Behavior")]
        [Description("Regex pattern to be matched when leaving the focus")]
        [DefaultValue("")]
        public string FinalRegularExpression
        {
            get
            {
                return finalRegexString;
            }
            set
            {
                if (!string.IsNullOrEmpty(value))
                    finalRegex = new Regex(value);
                else
                    finalRegex = null;
                finalRegexString = value;
            }
        }

        /// <summary>
        /// Require input. Checked as part of the final check.
        /// </summary>
        [Category("Behavior")]
        [Description("Require input. Checked as part of the final check.")]
        [DefaultValue(false)]
        public bool RequireInput
        {
            get
            {
                return requireInput;
            }
            set
            {
                requireInput = value;
            }
        }

        /// <summary>
        /// Trim the text when leaving the focus
        /// </summary>
        [Category("Behavior")]
        [Description("Trim the text when leaving the focus")]
        [DefaultValue(false)]
        public bool TrimOnLeaving
        {
            get
            {
                return trimOnLeaving;
            }
            set
            {
                trimOnLeaving = value;
            }
        }

        /// <summary>
        /// Description of the expected format to be displayed to the user
        /// </summary>
        [Category("Behavior")]
        [Description("Description of the expected format to be displayed to the user")]
        [DefaultValue("")]
        public string ExpectedFormatDescription
        {
            get
            {
                return expectedFormatDescription;
            }
            set
            {
                expectedFormatDescription = value;
            }
        }

        /// <summary>
        /// Defer the final checks. They will only be applied when the FinalCheck method is invoked.
        /// </summary>
        [Category("Behavior")]
        [Description("Defer the final checks. They will only be applied when the FinalCheck method is invoked.")]
        [DefaultValue(false)]
        public bool DeferFinalCheck
        {
            get { return deferFinalCheck; }
            set { deferFinalCheck = value; }
        }

        /// <summary>
        /// Adjust the height of the control depending on its contents
        /// </summary>
        [Category("Layout")]
        [Description("Adjust the height of the control depending on its contents")]
        [DefaultValue(false)]
        public bool AutoHeight
        {
            get
            {
                return autoHeight;
            }
            set
            {
                autoHeight = value;
                UpdateAutoHeight();
            }
        }

        protected override void OnTextChanged(EventArgs e)
        {
            bool accept = true;

            HideErrorNotice();
            if (type == ValidatingTextBoxType.Numeric)
            {
                string valid = "0123456789";
                foreach (char c in Text.ToCharArray())
                {
                    if (valid.IndexOf(c) == -1)
                    {
                        accept = false;
                        break;
                    }
                }
            }
            else if (type == ValidatingTextBoxType.ValidCharacters && !string.IsNullOrEmpty(validCharacters))
            {
                foreach (char c in Text.ToCharArray())
                {
                    if (validCharacters.IndexOf(c) == -1)
                    {
                        accept = false;
                        break;
                    }
                }
            }
            else if (type == ValidatingTextBoxType.RegularExpression && regex != null)
            {
                accept = regex.IsMatch(Text);
            }
            else if (type == ValidatingTextBoxType.Custom && ValidateText != null)
            {
                ValidatingTextBoxEventArgs e2 = new ValidatingTextBoxEventArgs(Text);
                ValidateText(this, e2);
                accept = e2.Accept;
            }
           
            if (!accept)
            {
                SystemSounds.Beep.Play();
                Console.Beep(50, 10);
                Text = prevText;
                SelectionStart = prevSelStart;
                SelectionLength = prevSelLen;
                ShowErrorNotice();
            }
            else
            {
                prevText = Text;
            }
           
            base.OnTextChanged(e);

            UpdateAutoHeight();
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            // Add some comfort here
            if (e.KeyCode == Keys.Back && !e.Alt && e.Control && !e.Shift ||
                e.KeyCode == Keys.W && !e.Alt && e.Control && !e.Shift)
            {
                // Delete word to the left
                int sel = SelectionStart;
                if (sel > 0)
                {
                    int pos = Text.Substring(0, sel - 1).LastIndexOfAny(new char[] { ' ', '\t' });
                    //if (pos == -1) pos = 0;
                    pos++;
                    Text = Text.Substring(0, pos) + Text.Substring(sel);
                    SelectionStart = pos;
                }
                e.SuppressKeyPress = e.Handled = true;
            }
            if (e.KeyCode == Keys.U && !e.Alt && e.Control && !e.Shift)
            {
                // Delete all
                Text = "";
                e.SuppressKeyPress = e.Handled = true;
            }
            if (e.KeyCode == Keys.A && !e.Alt && e.Control && !e.Shift)
            {
                // Select all
                SelectionStart = 0;
                SelectionLength = Text.Length;
                e.SuppressKeyPress = e.Handled = true;
            }
            if (e.KeyCode == Keys.Up && !e.Alt && e.Control && !e.Shift && Multiline)
            {
                // Scroll line up
                SendMessage(Handle, EM_SCROLL, new IntPtr(SB_LINEUP), IntPtr.Zero);
                e.SuppressKeyPress = e.Handled = true;
            }
            if (e.KeyCode == Keys.Down && !e.Alt && e.Control && !e.Shift && Multiline)
            {
                // Scroll line down
                SendMessage(Handle, EM_SCROLL, new IntPtr(SB_LINEDOWN), IntPtr.Zero);
                e.SuppressKeyPress = e.Handled = true;
            }

            prevSelStart = SelectionStart;
            prevSelLen = SelectionLength;

            base.OnKeyDown(e);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            prevSelStart = SelectionStart;
            prevSelLen = SelectionLength;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            prevSelStart = SelectionStart;
            prevSelLen = SelectionLength;
           
            base.OnMouseDown(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            if (trimOnLeaving)
            {
                Text = Text.Trim();
            }
           
            HideErrorNotice();
            if (!deferFinalCheck)
            {
                FinalCheck();
            }
           
            base.OnLeave(e);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            UpdateAutoHeight();
        }

        /// <summary>
        /// Performs the deferred final checks. Shows the error message, plays the beep sound and focuses the control if errors were found.
        /// </summary>
        /// <returns>true if the check is successful, false if errors were found</returns>
        public bool FinalCheck()
        {
            bool accept = true;

            HideErrorNotice();
            if (accept && requireInput && Text.Length == 0)
            {
                accept = false;
            }
            if (accept && finalRegex != null)
            {
                accept = finalRegex.IsMatch(Text);
            }
            if (accept && FinalValidateText != null)
            {
                ValidatingTextBoxEventArgs e2 = new ValidatingTextBoxEventArgs(Text);
                FinalValidateText(this, e2);
                accept = e2.Accept;
                Text = e2.Text;
            }

            if (!accept)
            {
                ShowErrorNotice();
                SystemSounds.Beep.Play();
                Console.Beep(50, 10);
                Focus();
            }
            return accept;
        }

        protected void ShowErrorNotice()
        {
            string msg;
            switch (CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)
            {
                case "de":
                    msg = "Die Eingabe hat ein ungültiges Format.";
                    if (!string.IsNullOrEmpty(expectedFormatDescription))
                        msg += "\r\nErwartet: " + expectedFormatDescription;
                    else if (Type == ValidatingTextBoxType.Numeric)
                        msg += "\r\nEs sind nur nummerische Eingaben (0-9) zulässig.";
                    if (requireInput)
                        msg += "\r\nEine Eingabe ist erforderlich.";
                    break;
                default:
                    msg = "The input format is invalid.";
                    if (!string.IsNullOrEmpty(expectedFormatDescription))
                        msg += "\r\nExpected: " + expectedFormatDescription;
                    else if (Type == ValidatingTextBoxType.Numeric)
                        msg += "\r\nOnly numeric values (0-9) are accepted.";
                    if (requireInput)
                        msg += "\r\nInput is required.";
                    break;
            }

            if (errorNoticeTooltip != null)
            {
                errorNoticeTooltip.Dispose();
            }
            errorNoticeTooltip = new ToolTip();
            errorNoticeTooltip.ForeColor = SystemColors.WindowText;
            errorNoticeTooltip.BackColor = MixedColor(SystemColors.Window, Color.Red, 0.25);
            errorNoticeTooltip.Show(msg, this, 0, Height, 10000);
        }

        protected void HideErrorNotice()
        {
            if (errorNoticeTooltip != null)
            {
                errorNoticeTooltip.Dispose();
            }
        }

        private void UpdateAutoHeight()
        {
            if (autoHeight && Multiline)
            {
                Size prefSize = GetPreferredSize(new Size(Width, 0));
                Height = prefSize.Height;
            }
        }

        #region Unclassified.WinApi
        [DllImport("user32")]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

        private const uint EM_SCROLL = 0xB5;
        private const uint SB_LINEUP = 0;
        private const uint SB_LINEDOWN = 1;
        #endregion

        #region Unclassified.Drawing.ColorMath
        private static Color MixedColor(Color color1, Color color2, double ratio)
        {
            int a = (int) Math.Round(color1.A * (1 - ratio) + color2.A * ratio);
            int r = (int) Math.Round(color1.R * (1 - ratio) + color2.R * ratio);
            int g = (int) Math.Round(color1.G * (1 - ratio) + color2.G * ratio);
            int b = (int) Math.Round(color1.B * (1 - ratio) + color2.B * ratio);
            return Color.FromArgb(a, r, g, b);
        }
        #endregion
    }
}

Änderungen

18. September 2009

Erste veröffentlichte Version

Licence, terms of use

This programme is freely available as source code and compiled version, without restrictions (“public domain”). There is no warranty, not even for merchantability or fitness for a particular purpose. I am not liable for any damage caused through appropriate or inappropriate use.

Ausblenden
Statistik wird geladen...