先看下实现界面效果:
实现的效果主要有:
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的技能点:比如:依赖属性,附件属性,数据绑定,自定义样式,模板,转化器等,真是一个学习的好例子!