WPF 显示验证错误的默认方式是在控件周围绘制红色边框。通常需要对此方法进行自定义,以通过其他方式来显示错误。而且,默认情况下不会显示与验证错误关联的错误消息。常见的要求是仅当存在验证错误时才在工具提示中显示错误消息。通过将 Styles 和一组与验证关联的附加属性进行组合,可以相当轻松地自定义验证错误显示。
前台xaml:
- <ResourceDictionary
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
- <!-- 应该在此定义资源字典条目。-->
- <LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" StartPoint="0,0" MappingMode="Absolute">
- <GradientStop Color="#ABADB3" Offset="0.05"/>
- <GradientStop Color="#E2E3EA" Offset="0.07"/>
- <GradientStop Color="#E3E9EF" Offset="1"/>
- </LinearGradientBrush>
- <ControlTemplate x:Key="validationTemplate">
- </ControlTemplate>
- <Style BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
- <Setter Property="Validation.ErrorTemplate" Value="{StaticResource validationTemplate}" />
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
- <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
- <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
- <Setter Property="BorderThickness" Value="1"/>
- <Setter Property="Padding" Value="1"/>
- <Setter Property="AllowDrop" Value="true"/>
- <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type TextBox}">
- <Grid x:Name="root">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*"/>
- <ColumnDefinition Width="1"/>
- </Grid.ColumnDefinitions>
- <!--<Border x:Name="Border" Background="White" BorderBrush="Gray" BorderThickness="0" Padding="2" CornerRadius="1">-->
- <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" RenderMouseOver="{TemplateBinding IsMouseOver}">
- <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
- </Microsoft_Windows_Themes:ListBoxChrome>
- <!--</Border>-->
- <Border x:Name="border" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed" HorizontalAlignment="Stretch" Margin="0" Width="Auto">
- <Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
- <Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
- <Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
- </Grid>
- </Border>
- <Popup x:Name="popup" Placement="Right" IsOpen="False">
- <Border x:Name="border1_Copy" Width="Auto" Height="Auto" Background="Red" BorderThickness="0" >
- <TextBlock TextWrapping="NoWrap" Text="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
- </Border>
- <!--<Grid Width="50" Height="20" Background="Red"/>-->
- </Popup>
- <!--<Popup x:Name="popup" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" IsOpen="False" StaysOpen="True" AllowsTransparency="True">
- <Border x:Name="border1_Copy" BorderThickness="1" Margin="0" Background="Red" CornerRadius="2" HorizontalAlignment="Stretch" Opacity="0" RenderTransformOrigin="0.5,0.5" Height="Auto" Width="Auto">
- <Border.RenderTransform>
- <TransformGroup>
- <ScaleTransform/>
- <SkewTransform/>
- <RotateTransform/>
- <TranslateTransform X="10"/>
- </TransformGroup>
- </Border.RenderTransform>
- <Border.Effect>
- <DropShadowEffect Direction="-90" BlurRadius="5" Color="#FF808080" ShadowDepth="1"/>
- </Border.Effect>
- <TextBlock TextWrapping="NoWrap" Text="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
- </Border>
- </Popup>-->
- </Grid>
- <ControlTemplate.Triggers>
- <Trigger Property="Validation.HasError" Value="True">
- <Setter Property="Visibility" TargetName="border" Value="Visible"/>
- </Trigger>
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="Validation.HasError" Value="True"/>
- <Condition Property="IsFocused" Value="True"/>
- </MultiTrigger.Conditions>
- <Setter Property="IsOpen" TargetName="popup" Value="True"/>
- </MultiTrigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
- <!--<Style TargetType="TextBox">
- <Style.Triggers>
- <Trigger Property="Validation.HasError"
- Value="True">
- <Setter Property="ToolTip">
- <Setter.Value>
- <Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
- </Setter.Value>
- </Setter>
- </Trigger>
- </Style.Triggers>
- </Style>-->
- </ResourceDictionary>
后台代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- namespace WpfApplication11111
- {
- /// <summary>
- /// UserControl2.xaml 的交互逻辑
- /// </summary>
- public partial class UserControl2 : UserControl
- {
- private UserInfo _UserInfo;
- public UserControl2()
- {
- InitializeComponent();
- this.Loaded += new RoutedEventHandler(UserControl2_Loaded);
- }
- void UserControl2_Loaded(object sender, RoutedEventArgs e)
- {
- _UserInfo = new UserInfo();
- this.DataContext = _UserInfo;
- }
- private void btnSave_Click(object sender, RoutedEventArgs e)
- {
- //txtName.Visibility = Visibility.Collapsed;
- UserControl1 _UserControl1 = new UserControl1();
- grid.Children.Add(_UserControl1);
- string _name = _UserInfo.Name;
- string _pass = _UserInfo.Pass;
- }
- }
- }
实体类:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- namespace WpfApplication11111
- {
- public class UserInfo : ValidationUtility, INotifyPropertyChanged
- {
- #region 数据更新通知
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChange(string propertyName)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- #endregion
- private string _Name;
- [Required(ErrorMessage = "[登录名]内容不能为空!")]
- [StringLength(255, ErrorMessage = "[登录名]内容最大允许255个字符!")]
- [RegularExpression("^[A-Za-z0-9]+$", ErrorMessage = "[登录名]格式不正确!")]
- /// <summary>
- ///
- /// </summary>
- public string Name
- {
- get { return _Name; }
- set
- {
- //Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = "Name" });
- //if (string.IsNullOrEmpty(value))
- //{
- // throw new Exception("用户名不能为空.");
- //}
- _Name = value;
- NotifyPropertyChange("Name");
- }
- }
- private string _Pass;
- [Required(ErrorMessage = "[密码]内容不能为空!")]
- [StringLength(255, ErrorMessage = "[密码]内容最大允许255个字符!")]
- [RegularExpression("^[A-Za-z0-9]+$", ErrorMessage = "[密码]格式不正确!")]
- /// <summary>
- ///
- /// </summary>
- public string Pass
- {
- get { return _Pass; }
- set
- {
- //if (string.IsNullOrEmpty(value))
- //{
- // throw new Exception("密码不能为空.");
- //}
- _Pass = value;
- NotifyPropertyChange("Pass");
- }
- }
- }
- }
ValidationUtility.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.ComponentModel;
- using System.Reflection;
- using System.ComponentModel.DataAnnotations;
- namespace WpfApplication11111
- {
- public class ValidationUtility : IDataErrorInfo
- {
- public string Error
- {
- get { return _error; }
- }
- public string _error;
- public string this[string columnName]
- {
- get
- {
- Type tp = this.GetType();
- PropertyInfo pi = tp.GetProperty(columnName);
- var value = pi.GetValue(this, null);
- object[] Attributes = pi.GetCustomAttributes(false);
- if (Attributes != null && Attributes.Length > 0)
- {
- foreach (object attribute in Attributes)
- {
- if (attribute is ValidationAttribute)
- {
- ValidationAttribute vAttribute = attribute as ValidationAttribute;
- if (!vAttribute.IsValid(value))
- {
- _error = vAttribute.ErrorMessage;
- return _error;
- }
- }
- }
- }
- return null;
- }
- }
- }
- }
追加PasswordBox验证
-
- <Style TargetType="{x:Type PasswordBox}">
- <Setter Property="Validation.ErrorTemplate" Value="{StaticResource validationTemplate}" />
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
- <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
- <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
- <Setter Property="FontFamily" Value="Times New Roman"/>
- <Setter Property="PasswordChar" Value="●"/>
- <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
- <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
- <Setter Property="BorderThickness" Value="1"/>
- <Setter Property="HorizontalContentAlignment" Value="Left"/>
- <Setter Property="Padding" Value="1"/>
- <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
- <Setter Property="AllowDrop" Value="true"/>
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="{x:Type PasswordBox}">
- <Grid x:Name="root">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*"/>
- <ColumnDefinition Width="1"/>
- </Grid.ColumnDefinitions>
- <!--<Border x:Name="Border" Background="White" BorderBrush="Gray" BorderThickness="0" Padding="2" CornerRadius="1">-->
- <Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" RenderMouseOver="{TemplateBinding IsMouseOver}">
- <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
- </Microsoft_Windows_Themes:ListBoxChrome>
- <!--</Border>-->
- <Border x:Name="border" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1" Visibility="Collapsed" HorizontalAlignment="Stretch" Margin="0" Width="Auto">
- <Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12">
- <Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/>
- <Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/>
- </Grid>
- </Border>
- <Popup x:Name="popup" Placement="Right" IsOpen="False">
- <Border x:Name="border1_Copy" Width="Auto" Height="Auto" Background="Red" BorderThickness="0" >
- <TextBlock TextWrapping="NoWrap" Text="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
- </Border>
- </Popup>
- </Grid>
- <ControlTemplate.Triggers>
- <Trigger Property="Validation.HasError" Value="True">
- <Setter Property="Visibility" TargetName="border" Value="Visible"/>
- </Trigger>
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="Validation.HasError" Value="True"/>
- <Condition Property="IsFocused" Value="True"/>
- </MultiTrigger.Conditions>
- <Setter Property="IsOpen" TargetName="popup" Value="True"/>
- </MultiTrigger>
- </ControlTemplate.Triggers>
- </ControlTemplate>
- </Setter.Value>
- </Setter>
- </Style>
-
代码下载地址:
http://download.csdn.net/detail/hwt0101/5070730