WPF 按钮绑定TextBox的 Validation.HasError

先看下实现界面效果:

WPF 按钮绑定TextBox的 Validation.HasError_第1张图片

实现的效果主要有:

1:输入用户姓名验证不通过(/^[\u4e00-\u9fa5\w]{3,16}$/),文本框边框显示红色,鼠标悬浮有ToolTip提示错误信息;

2:当输入验证不通过时,保存按钮不可使用;

这里实现步骤如下:

1:定义带清除按钮的文本框;


   
                
                    
                    
                        
                    
                
                
                    
                
    


其中ImageTextBox类定义为:

public class ImageTextBox : TextBox
    {

        public ImageSource ImageSource
        {
            get { return (ImageSource)GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ImageSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ImageTextBox), new PropertyMetadata(null));


        public double ImageWidth
        {
            get { return (double)GetValue(ImageWidthProperty); }
            set { SetValue(ImageWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ImageWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageWidthProperty =
            DependencyProperty.Register("ImageWidth", typeof(double), typeof(ImageTextBox), new PropertyMetadata(20d));

        public double ImageHeight
        {
            get { return (double)GetValue(ImageHeightProperty); }
            set { SetValue(ImageHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ImageHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageHeightProperty =
            DependencyProperty.Register("ImageHeight", typeof(double), typeof(ImageTextBox), new PropertyMetadata(20d));


        public Thickness ImageMargin
        {
            get { return (Thickness)GetValue(ImageMarginProperty); }
            set { SetValue(ImageMarginProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ImageMargin.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageMarginProperty =
            DependencyProperty.Register("ImageMargin", typeof(Thickness), typeof(ImageTextBox), new PropertyMetadata(new Thickness()));


    }

    public class TextBoxHelper
    {
        #region 附加属性 Clearable
        public static readonly DependencyProperty ClearableProperty =
            DependencyProperty.RegisterAttached("Clearable", typeof(bool), typeof(TextBoxHelper), new PropertyMetadata(false, ClearText));


        public static bool GetClearable(DependencyObject obj)
        {
            return (bool)obj.GetValue(ClearableProperty);
        }

        public static void SetClearable(DependencyObject obj, bool value)
        {
            obj.SetValue(ClearableProperty, value);
        }

        #endregion

        #region 
      
        private static void ClearText(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Button btn = d as Button;
            if (d != null && e.OldValue != e.NewValue)
            {
                btn.Click -= ClearTextClicked;
                if ((bool)e.NewValue)
                {
                    btn.Click += ClearTextClicked;
                }
            }
        }
      
        public static void ClearTextClicked(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            if (btn != null)
            {
                var parent = VisualTreeHelper.GetParent(btn);
                while (!(parent is TextBox))
                {
                    parent = VisualTreeHelper.GetParent(parent);
                }
                TextBox txt = parent as TextBox;
                if (txt != null)
                {
                    txt.Clear();
                }
            }
        }

        #endregion
    }

    public class UserNameValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (string.IsNullOrEmpty(value.ToString()))
                return new ValidationResult(false, "userNameRequired".ToGlobal());
            string rule = @"^[\u4e00-\u9fa5\w]{2,16}$";
            bool result= Validator.IsMatch(value.ToString(), rule);
            if (!result)
            {
                return new ValidationResult(false, "userNameValidation".ToGlobal());
            }
            return new ValidationResult(true, null);
        }
    }

2:  XAML中关键的布局为:

一个文本框,一个编辑文本框,处于同一个位置,用显示和隐藏来控制,用了一个ToggleButton来实现,文本框的显示和隐藏同ToggleButton的IsChecked属于相绑定;


                
                    
                        
                            
                        
                    
                
            

这里用了一个WPF中的附加属性,ctrl:FocusExtension.IsFocused 来控制文本输入框显示时自动获取光标:

public static class FocusExtension
    {
        public static bool GetIsFocused(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsFocusedProperty);
        }


        public static void SetIsFocused(DependencyObject obj, bool value)
        {
            obj.SetValue(IsFocusedProperty, value);
        }


        public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.RegisterAttached(
             "IsFocused", typeof(bool), typeof(FocusExtension),
             new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));


        private static void OnIsFocusedPropertyChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {

            var uie = (UIElement)d;
         
            if ((bool)e.NewValue)
            {
                bool result = uie.Focus();
                if (!result)
                {
                    if (d is FrameworkElement)
                    {
                        var fe = d as FrameworkElement;
                        if (!fe.IsLoaded)
                        {
                            RoutedEventHandler loadedHandler = null;
                            loadedHandler = (sender, args) =>
                            {
                                (sender as FrameworkElement).Focus();
                                (sender as FrameworkElement).Loaded -= loadedHandler;
                            };
                            fe.Loaded += loadedHandler;
                        }
                    }
                }
            }
        }
    }

当文本框输入错误时Validation.HasError=true, 此时绑定到ToggleButton的IsEnabled 属性,

IsEnabled="{Binding ElementName=txtUserName, Path=(Validation.HasError),

重点是红色的部分,一定要用括号括起来,否则没有效果,在这里折腾了一会,没有找到原因,后来发现是这里的问题,特别注意!

  

总结下这个功能用到了蛮多WPF的技能点:比如:依赖属性,附件属性,数据绑定,自定义样式,模板,转化器等,真是一个学习的好例子!


你可能感兴趣的:(WPF,C#.NET)