自带输入规则、占位符、警告边框的自定义文本输入框

需求

 1.设定四种输入规则

a,无验证

  输入框可以输入任意字符

b,大写

  输入框可以输入任意字符且小写字母自动转换为大写

c,小写

输入框可以输入任意字符且大写字母自动转换为大写

d,数字

输入框只能输入数字

2.警告边框

当文本输入框文本不符合输入规则时显示警告边框

3.占位符

当文本输入框文本为空且无焦点时,显示占位符

4.其他

a.字体大小随输入框高度变化
b.增加一个按下enter键的事件

实现

1.输入规则

继承控件,利用重载事件中的逻辑处理实现

2.提示边框、占位符

利用Adorner对自定义的文本框进行装饰来实现

Tips

1.Adorner

需要理解Adorner的原理以及使用时的一些注意事项

2.路由事件

注意WPF中事件的传递顺序

3.输入法

必要时验证输入法或者禁用输入法(数字规则)

4.TextBox

需要详细理解系统文本框原理、事件流程
--------------------------------------------------------------------------------以下为代码,没有仔细整理,见谅------------------------------------------------------------------------------------------------
样式模版:
 <Style TargetType="{x:Type local:XTextInput}">
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
        <Setter Property="BorderBrush" Value="#FFABADB3"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="AllowDrop" Value="True"/>
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="MaxLength" Value="100"/>
        <Setter Property="TextWrapping" Value="NoWrap"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <AdornerDecorator>
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                        </Border>
                    </AdornerDecorator>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

    </Style>

Adorner
    #region 占位符装饰器

    public class PlaceHolderAdorner : Adorner
    {

        private TextBlock tipTextblock;
        private TextBox host;
        private VisualCollection visCollec;

        public PlaceHolderAdorner(TextBox element, string tipString,Brush tipColor,FontStyle tipFontStyle,FontWeight tipFontWeight)
            : base(element)
        {
            if (element == null) return;
            host = element;

            visCollec = new VisualCollection(this);
            tipTextblock = new TextBlock();

            tipTextblock.Style = null;
            tipTextblock.FontSize = element.FontSize;
            tipTextblock.Height = element.Height - element.BorderThickness.Bottom - element.BorderThickness.Top;
            tipTextblock.Width = element.Width - element.BorderThickness.Left - element.BorderThickness.Right;
            tipTextblock.FontFamily = element.FontFamily;
            tipTextblock.FontStretch = element.FontStretch;
             tipTextblock.FontWeight =tipFontWeight;
            tipTextblock.FontStyle = tipFontStyle;
            tipTextblock.Foreground = tipColor;
            tipTextblock.Text = tipString;
            tipTextblock.IsHitTestVisible = false;

            visCollec.Add(tipTextblock);
        }


        protected override int VisualChildrenCount
        {
            get
            {
                return visCollec.Count;
            }
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            tipTextblock.Arrange(new Rect(new Point(host.BorderThickness.Left, host.BorderThickness.Top), finalSize));
            return finalSize;
        }

        protected override Visual GetVisualChild(int index)
        {
            return visCollec[index];
        }

    }

    #endregion

    #region 特殊边框装饰器

    public class BorderAdorner : Adorner
    {
        private VisualCollection visCollec;
        private Border warnBorder;

        public BorderAdorner(TextBox ele, Brush boederColor)
            : base(ele)
        {

            if (ele == null) return;

           
            visCollec = new VisualCollection(this);
            warnBorder = new Border();
            warnBorder.BorderBrush = boederColor;
            warnBorder.Width = ele.Width;
            warnBorder.Height = ele.Height;
   
            warnBorder.BorderThickness = ele.BorderThickness == new Thickness(0) ? new Thickness(1, 1, 1, 1) : ele.BorderThickness;

            warnBorder.IsHitTestVisible = false;
            visCollec.Add(warnBorder);
        }


        protected override int VisualChildrenCount
        {
            get
            {
                return visCollec.Count;
            }
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            warnBorder.Arrange(new Rect(new Point(0, 0), finalSize));
            return finalSize;
        }

        protected override Visual GetVisualChild(int index)
        {
            return visCollec[index];
        }
    }

    #endregion

文本输入框实现
    public class XTextInput : TextBox
    {
        static XTextInput()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(XTextInput), new FrameworkPropertyMetadata(typeof(XTextInput)));
        }

        private AdornerLayer layer = null;

        #region add properties

        [Category("CX")]
        [Description("获取文本输入是否符合输入当前规则")]
        public bool IsVailedText
        {
            get { return IsVailedString(Text); }
        }

        #region Add Property:PlaceHolderText
        public static readonly DependencyProperty PlaceHolderTextProperty =
            DependencyProperty.Register("PlaceHolderText", typeof(string), typeof(XTextInput), new PropertyMetadata("请输入"));

        [Category("CX")]
        [Description("提示信息,超过20自动截取")]
        public string PlaceHolderText
        {
            get { return GetValue(PlaceHolderTextProperty).ToString(); }
            set
            {
                SetValue(PlaceHolderTextProperty, value.Length > 20 ? value.Substring(0, 20) : value);
                SetPlaceHolder();
            }
        }
        #endregion

        #region Add Property:PlaceHolderBrush

        public static readonly DependencyProperty PlaceHolderBrushProperty =
            DependencyProperty.Register("PlaceHolderBrush", typeof(Brush), typeof(XTextInput), new PropertyMetadata(Brushes.DarkGray));

        [Category("CX")]
        [Description("提示信息文字颜色")]
        public Brush PlaceHolderBrush
        {
            get { return (Brush)GetValue(PlaceHolderBrushProperty); }
            set
            {
                SetValue(PlaceHolderBrushProperty, value);
                SetPlaceHolder();
            }
        }

        #endregion

        #region Add Property:PlaceHolderFontStyle

        public static readonly DependencyProperty PlaceHolderFontStyleProperty =
            DependencyProperty.Register("PlaceHolderFontStyle", typeof(FontStyle), typeof(XTextInput), new PropertyMetadata(FontStyles.Italic));

        [Category("CX")]
        [Description("提示信息文字字体样式")]
        public FontStyle PlaceHolderFontStyle
        {
            get { return (FontStyle)GetValue(PlaceHolderFontStyleProperty); }
            set
            {
                SetValue(PlaceHolderFontStyleProperty, value);
                SetPlaceHolder();
            }
        }

        #endregion

        #region Add Property:PlaceHolderFontWeight

        public static readonly DependencyProperty PlaceHolderFontWeightProperty =
            DependencyProperty.Register("PlaceHolderFontWeight", typeof(FontWeight), typeof(XTextInput), new PropertyMetadata(FontWeights.Normal));

        [Category("CX")]
        [Description("提示信息文字字体粗细")]
        public FontWeight PlaceHolderFontWeight
        {
            get { return (FontWeight)GetValue(PlaceHolderFontWeightProperty); }
            set
            {
                SetValue(PlaceHolderFontWeightProperty, value);
                SetPlaceHolder();
            }
        }

        #endregion

        #region Add Property:WarnBrush

        public static readonly DependencyProperty WarnBrushProperty =
            DependencyProperty.Register("WarnBrush", typeof(Brush), typeof(XTextInput),
                                        new PropertyMetadata(Brushes.Red));

        [Category("CX")]
        [Description("文本不符合规则时警告边框样式")]
        public Brush WarnBrush
        {
            get { return (Brush)GetValue(WarnBrushProperty); }
            set
            {
                SetValue(WarnBrushProperty, value);
                SetWarnBorder();
            }
        }

        #endregion

        #region Add Property:TextRule

        public static readonly DependencyProperty TextRuleProperty =
            DependencyProperty.Register("TextRule", typeof(XTextRule), typeof(XTextInput),
                                        new FrameworkPropertyMetadata(XTextRule.AllCase));

        [Category("CX")]
        [Description("文本规则,包括:任意字符、大写、小写、数字,默认任意字符")]
        public XTextRule TextRule
        {
            get { return (XTextRule)GetValue(TextRuleProperty); }
            set
            {
                SetValue(TextRuleProperty, value);
                InputMethod.SetIsInputMethodEnabled(this, TextRule == XTextRule.OnlyNumber ? false : true);
                SetWarnBorder();
            }
        }
        #endregion

        #region Add Event:EnterCompleted
        public delegate void EnterCompletedHandle(object sender);

        [Description("输入完成事件,键入Enter键触发")]
        public event EnterCompletedHandle EnterCompleted;
        #endregion

        #endregion

        #region override

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            layer = AdornerLayer.GetAdornerLayer(this);
            CommandBinding pasteCommand = new CommandBinding(ApplicationCommands.Paste);
            pasteCommand.PreviewCanExecute += (sender, e) =>
            {
                e.CanExecute = true;
            };
            pasteCommand.CanExecute += (sender, e) =>
            {
                e.CanExecute = true;
            };

            pasteCommand.PreviewExecuted += pasteCommand_PreviewExecuted;
            CommandBindings.Add(pasteCommand);//加入粘贴命令
            InputMethod.SetIsInputMethodEnabled(this, TextRule == XTextRule.OnlyNumber ? false : true);
            SetWarnBorder();
        }

        void pasteCommand_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (!System.Windows.Clipboard.ContainsText()) return;
            string s = System.Windows.Clipboard.GetText();
            switch (TextRule)
            {
                case XTextRule.AllCase:
                    if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
                    {
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
                        newText = newText.Insert(this.SelectionStart, s);
                        int endIndex = this.SelectionStart + s.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    else
                    {
                        int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
                        var str = s.Substring(0, s.Length - overFolwLength);
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str);
                        int endIndex = this.SelectionStart + str.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    break;
                case XTextRule.UpperCase:
                    if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
                    {
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
                        newText = newText.Insert(this.SelectionStart, s.ToUpper());
                        int endIndex = this.SelectionStart + s.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    else
                    {
                        int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
                        var str = s.Substring(0, s.Length - overFolwLength);
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToUpper());
                        int endIndex = this.SelectionStart + str.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    break;
                case XTextRule.OnlyNumber:
                    if (s.Any(p => !Char.IsDigit(p)))
                        e.Handled = true;
                    else
                    {
                        if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
                        {
                            string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
                            newText = newText.Insert(this.SelectionStart, s.ToLower());
                            int endIndex = this.SelectionStart + s.Length;
                            this.Text = newText;
                            CaretIndex = endIndex;
                        }
                        else
                        {
                            int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
                            var str = s.Substring(0, s.Length - overFolwLength);
                            string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToLower());
                            int endIndex = this.SelectionStart + str.Length;
                            this.Text = newText;
                            CaretIndex = endIndex;
                        }
                    }

                    break;
                case XTextRule.LowerCase:
                    if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
                    {
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
                        newText = newText.Insert(this.SelectionStart, s.ToLower());
                        int endIndex = this.SelectionStart + s.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    else
                    {
                        int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
                        var str = s.Substring(0, s.Length - overFolwLength);
                        string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToLower());
                        int endIndex = this.SelectionStart + str.Length;
                        this.Text = newText;
                        CaretIndex = endIndex;
                    }
                    break;
            }
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            FontSize = sizeInfo.NewSize.Height * 0.6;
            SetPlaceHolder();
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            if (e.Key == Key.Enter && e.ImeProcessedKey != Key.ImeProcessed)
                if (EnterCompleted != null)
                    EnterCompleted(this);

            if (TextRule == XTextRule.OnlyNumber)
            {
                if(e.Key==Key.Space)
                    e.Handled = true;
            }
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);
            SelectAll();
            SetPlaceHolder();
        }

        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            SetPlaceHolder();
            SetWarnBorder();
        }

        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            base.OnPreviewTextInput(e);
            string str = e.Text;
            int overFlow = Text.Length - this.SelectedText.Length + e.Text.Length - MaxLength;
            if (overFlow > 0)
            {
                if (overFlow == str.Length) return;
                else
                    str = e.Text.Substring(0, overFlow);
            }
            switch (TextRule)
            {
                case XTextRule.AllCase:
                    e.Handled = false;
                    break;
                case XTextRule.UpperCase:
                    if (!IsVailedString(str))
                    {
                        string newString = Text.Remove(this.SelectionStart, this.SelectionLength).Insert(
                                                  this.SelectionStart, str.ToUpper());
                        int index = CaretIndex;
                        Text = newString;
                        CaretIndex = index + str.Length;
                        e.Handled = true;
                    }
                    else
                        e.Handled = false;
                    break;
                case XTextRule.LowerCase:
                    if (!IsVailedString(str))
                    {
                        string newString = Text.Remove(this.SelectionStart, this.SelectionLength).Insert(
                           this.SelectionStart, str.ToLower());
                        int index = CaretIndex;
                        Text = newString;
                        CaretIndex = index + str.Length;
                        e.Handled = true;
                    }
                    else
                        e.Handled = false;
                    break;
                case XTextRule.OnlyNumber:
                    if (e.Text.Any(p => !Char.IsDigit(p)))
                        e.Handled = true;
                    else
                    {
                        e.Handled = false;
                    }
                    break;
            }
        }

        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            base.OnTextChanged(e);
            if (layer != null)
                SetWarnBorder();
        }

        #endregion

        #region function

        /// <summary>
        /// 判断是否符合当前规则
        /// </summary>
        /// <param name="chars"></param>
        /// <returns></returns>
        private bool IsVailedString(string chars)
        {
            switch (TextRule)
            {
                case XTextRule.LowerCase:
                    return chars.Any(p => Char.IsUpper(p)) ? false : true;
                case XTextRule.OnlyNumber:
                    return chars.All(p => Char.IsDigit(p)) ? true : false;
                case XTextRule.UpperCase:
                    return chars.Any(p => Char.IsLower(p)) ? false : true;
                default:
                    return true;
            }
        }

        /// <summary>
        /// 设置占位符装饰器
        /// </summary>
        private void SetPlaceHolder()
        {
            if (Text.Length == 0 && !IsFocused)
            {
                var tmp = layer.GetAdorners(this);
                if (tmp != null)
                {
                    foreach (var m in tmp)
                        if (m is PlaceHolderAdorner) layer.Remove(m);
                }
                PlaceHolderAdorner p = new PlaceHolderAdorner(this, PlaceHolderText, PlaceHolderBrush, PlaceHolderFontStyle, PlaceHolderFontWeight);
                layer.Add(p);
            }
            else
            {
                var tmp = layer.GetAdorners(this);
                if (tmp != null)
                {
                    foreach (var m in tmp)
                        if (m is PlaceHolderAdorner) layer.Remove(m);
                }
            }
        }

        /// <summary>
        /// 设置边框装饰器
        /// </summary>
        private void SetWarnBorder()
        {
            if (IsVailedString(Text))
            {
                var tmp = layer.GetAdorners(this);
                if (tmp != null)
                {
                    foreach (var m in tmp)
                        if (m is BorderAdorner) layer.Remove(m);
                }

            }
            else
            {
                var tmp = layer.GetAdorners(this);
                if (tmp != null)
                {
                    foreach (var m in tmp)
                        if (m is BorderAdorner) layer.Remove(m);
                }
                BorderAdorner p = new BorderAdorner(this, WarnBrush);
                layer.Add(p);
            }
        }

        #endregion
    }

    /// <summary>
    /// XTextInput 文本输入规则
    /// </summary>
    public enum XTextRule
    {
        /// <summary>
        /// 大小写混合
        /// </summary>
        AllCase = 0,
        /// <summary>
        /// 全部大写
        /// </summary>
        UpperCase = 1,
        /// <summary>
        /// 全部小写
        /// </summary>
        LowerCase = 2,
        /// <summary>
        /// 全部数字
        /// </summary>
        OnlyNumber = 3
    }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值