C# WinForm移除非法字符的输入框

C# WinForm移除非法字符的输入框

文章目录







namespace System.Windows.Forms
{
    using System.ComponentModel;

    /// <summary>
    /// 支持移除 非法字符 的输入框。
    /// </summary>
    public class RemoveInvalidCharTextBox : TextBox
    {
        /// <summary>
        /// 测试代码:设置 有效字符 为 整数 的字符。
        /// </summary>
        [System.Diagnostics.Conditional("DEBUG")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("", "IDE0017")]
        public static void TestValidCharAsInteger()
        {
            var form = new System.Windows.Forms.Form();
            var textBox = new RemoveInvalidCharTextBox();
            textBox.Dock =System.Windows.Forms.DockStyle.Top;
            textBox.UpdateValidCharAsInteger();
            form.Controls.Add(textBox);
            System.Windows.Forms.Application.Run(form);
        }

        public RemoveInvalidCharTextBox()
        {
            RemoveCharService = new TextBoxBaseRemoveCharService(this);
        }

        protected override void OnTextChanged(EventArgs e)
        {
            RemoveCharService?.OnTextChangedBefore();
            base.OnTextChanged(e);
        }

        /// <summary>
        /// 更新 有效字符 为 整数 的字符。
        /// </summary>
        public void UpdateValidCharAsInteger()
        {
            RemoveCharService.UpdateValidCharAsInteger();
        }

        private TextBoxBaseRemoveCharService RemoveCharService { get; set; }

        /// <summary>
        /// 禁止移除非法字符。
        /// </summary>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [DefaultValue(false)]
        public bool DisabledRemoveInvalidChars
        {
            get { return RemoveCharService.DisabledRemoveInvalidChars; }
            set { RemoveCharService.DisabledRemoveInvalidChars = value; }
        }
    }

}







namespace System.Windows.Forms
{
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Text;

    /// <summary>
    /// 移除非法字符的服务。
    /// </summary>
    public class TextBoxBaseRemoveCharService
    {
        public TextBoxBaseRemoveCharService(TextBoxBase textBox)
        {
            this.TextBox = textBox;
        }

        #region 初始化函数。

        /// <summary>
        /// 更新 有效字符 为 整数 的字符。
        /// </summary>
        public void UpdateValidCharAsInteger()
        {
            ValidChars = GetIntegerChars();
        }

        /// <summary>
        /// 整数 的 字符。
        /// </summary>
        /// <returns></returns>
        public static IList<char> GetIntegerChars()
        {
            return new char[]
            {
                '0',
                '1',
                '2',
                '3',
                '4',
                '5',
                '6',
                '7',
                '8',
                '9',
            };
        }

        /// <summary>
        /// 更新 无效字符 为 换行 的字符。
        /// </summary>
        public void UpdateInvalidCharAsNewLine()
        {
            InvalidChars = GetNewLineChars();
        }

        /// <summary>
        /// 换行 的字符。
        /// </summary>
        /// <returns></returns>
        public static IList<char> GetNewLineChars()
        {
            return new char[]
            {
                '\v',
                '\r',
                '\n',
            };
        }

        #endregion 初始化函数。

        #region 移除非法字符。

        /// <summary>
        /// 在 调用 <see cref="TextBoxBase.OnTextChanged(EventArgs)"/> 前执行。 <br />
        /// 注意:在重载函数中,调用本函数,本函数所属的对象可能为 null 。
        /// 所以,调用示例为 RemoveCharService?.OnTextChangedBefore()
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("", "S134")]
        public void OnTextChangedBefore()
        {
            var safeValidChars = ValidChars;
            var hasValidChars = safeValidChars != null && safeValidChars.Count > 0;
            var safeInvalidChars = InvalidChars;
            var hasInvalidChars = safeInvalidChars != null && safeInvalidChars.Count > 0;
            if (DisabledRemoveInvalidChars || (!hasValidChars && !hasInvalidChars))
            {
                return;
            }

            string oldText = TextBox.Text;
            if (string.IsNullOrEmpty(oldText))
            {
                return;
            }

            StringBuilder newBuilder = new StringBuilder(oldText.Length);
            List<int> removeIndexes = new List<int>(16);
            int index = -1;
            foreach (var ch in oldText)
            {
                ++index;
                if (hasValidChars)
                {
                    if (!safeValidChars.Contains(ch))
                    {
                        removeIndexes.Add(index);
                    }
                    else
                    {
                        newBuilder.Append(ch);
                    }
                }
                // 等价 else if (hasInvalidChars)
                else
                {
                    if (safeInvalidChars.Contains(ch))
                    {
                        removeIndexes.Add(index);
                    }
                    else
                    {
                        newBuilder.Append(ch);
                    }
                }
            }
            OnTextChangedBeforeCore(oldText, newBuilder, removeIndexes);
        }

        private void OnTextChangedBeforeCore(string oldText, StringBuilder newBuilder, List<int> removeIndexes)
        {
            string newText = newBuilder.ToString();
            if (newText != oldText)
            {
                TextBox.SuspendLayout();
                try
                {
                    // 处理重命名时,当输入非法字符时,会全选名称的问题。
                    int selectionStartOld = 0;
                    int selectionLengthOld = 0;
                    var isReadySelection = !string.IsNullOrEmpty(newText) &&
                        ReadySelect(out selectionStartOld, out selectionLengthOld);

                    TextBox.Text = newText;

                    // 处理重命名时,当输入非法字符时,会全选名称的问题。
                    if (isReadySelection)
                    {
                        selectionLengthOld -= MatchIndexCount(removeIndexes, selectionStartOld - 1, selectionStartOld + selectionLengthOld);
                        selectionStartOld -= MatchIndexCount(removeIndexes, -1, selectionStartOld);
                        GetSafeSelect(newText.Length, ref selectionStartOld, ref selectionLengthOld);
                        UpdateSelect(selectionStartOld, selectionLengthOld);
                    }
                }
                finally
                {
                    TextBox.ResumeLayout();
                }
            }
        }

        private int MatchIndexCount(IList<int> indexList, int minIndex, int maxIndex)
        {
            int count = 0;
            foreach (var index in indexList)
            {
                if (index > minIndex && index < maxIndex)
                {
                    ++count;
                }
            }
            return count;
        }

        private bool ReadySelect(out int selectionStart, out int selectionLength)
        {
            bool isSuccessCompleted;
            try
            {
                selectionStart = TextBox.SelectionStart;
                selectionLength = TextBox.SelectionLength;
                isSuccessCompleted = true;
            }
            catch (Exception ex)
            {
                TraceException("Ready selection exception: ", ex);
                selectionStart = 0;
                selectionLength = 0;
                isSuccessCompleted = false;
            }
            return isSuccessCompleted;
        }

        private void UpdateSelect(int selectionStart, int selectionLength)
        {
            try
            {
                TextBox.Select(selectionStart, selectionLength);
            }
            catch (Exception ex)
            {
                TraceException("Update selection exception: ", ex);
            }
        }

        private void GetSafeSelect(int length, ref int selectionStart, ref int selectionLength)
        {
            if (selectionStart > length)
            {
                selectionStart = length;
            }

            if (selectionLength > length)
            {
                selectionLength = length;
            }
        }

        #endregion 移除非法字符。

        private static void TraceException(string message, Exception ex)
        {
            System.Diagnostics.Trace.WriteLineIf(true, message + ex);
        }

        /// <summary>
        /// 禁止移除非法字符。
        /// </summary>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [DefaultValue(false)]
        public bool DisabledRemoveInvalidChars { get; set; }

        /// <summary>
        /// 有效字符。 <br />
        /// 注意:<see cref="ValidChars"/> 和 <see cref="InvalidChars"/> 最多一个不为 null 。
        /// </summary>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [DefaultValue(null)]
        private IList<char> ValidChars
        {
            get { return validChars; }
            set
            {
                if (validChars != value)
                {
                    if (value != null)
                    {
                        InvalidChars = null;
                    }
                    validChars = value;
                }
            }
        }

        private IList<char> validChars;

        /// <summary>
        /// 无效字符。 <br />
        /// 注意:<see cref="ValidChars"/> 和 <see cref="InvalidChars"/> 最多一个不为 null 。
        /// </summary>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [DefaultValue(null)]
        private IList<char> InvalidChars
        {
            get { return invalidChars; }
            set
            {
                if (invalidChars != value)
                {
                    if (value != null)
                    {
                        ValidChars = null;
                    }
                    invalidChars = value;
                }
            }
        }

        private IList<char> invalidChars;

        private TextBoxBase TextBox { get; set; }

    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值