/// <summary> /// 验证类型枚举 /// </summary> public enum ValidateTypeEmnu { /// <summary> /// 必填项目验证 /// </summary> MustFill, /// <summary> /// Email验证 /// </summary> Email, /// <summary> /// 数字验证 /// </summary> Numeral } /// <summary> /// SLTextBox /// </summary> [TemplatePart(Name = SLTextBox.PART_MustFillText, Type = typeof(TextBlock))] [TemplatePart(Name = SLTextBox.PART_EditImage, Type = typeof(Image))] [TemplateVisualState(Name = VSMSTATE_InvalidFocused, GroupName = VSMGROUP_Validation)] [TemplateVisualState(Name = VSMSTATE_InvalidUnfocused, GroupName = VSMGROUP_Validation)] public class SLTextBox : System.Windows.Controls.TextBox { private const string PART_MustFillText = "txtMustFill"; private const string PART_EditImage = "EditGlyph"; private const string VSMSTATE_InvalidFocused = "InvalidFocused"; private const string VSMSTATE_InvalidUnfocused = "InvalidUnfocused"; private const string VSMGROUP_Validation = "ValidationStates"; private TextBlock mustFillText; private Image editImage; private string promptMsg = string.Empty; /// <summary> /// 提示语 /// </summary> public string PromptMsg { get { return promptMsg; } set { promptMsg = value; } } #region 编辑图标 private string _editImageSource = string.Empty; /// <summary> /// EditImageSource /// </summary> public string EditImageSource { get { return _editImageSource; } set { _editImageSource = value; } } /// <summary> /// EditImageSource /// </summary> public static readonly DependencyProperty EditImageSourceProperty = DependencyProperty.Register( "EditImageSource", typeof(string), typeof(SLTextBox), new PropertyMetadata(string.Empty, EditImageSourcePropertyChanged)); private static void EditImageSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { SLTextBox txt = (SLTextBox)d; txt.EditImageSource = e.NewValue.ToString(); } #endregion #region 必填项 private bool _isMustFill = false; /// <summary> /// IsMustFill /// </summary> public bool IsMustFill { get { return _isMustFill; } set { _isMustFill = value; } } /// <summary> /// IsMustFill /// </summary> public static readonly DependencyProperty IsMustFillProperty = DependencyProperty.Register( "IsMustFill", typeof(bool), typeof(SLTextBox), new PropertyMetadata(false, IsMustFillPropertyChanged)); private static void IsMustFillPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { SLTextBox txt = (SLTextBox)d; txt.IsMustFill = (bool)e.NewValue; } #endregion /// <summary> /// SLTextBox /// </summary> public SLTextBox() { this.DefaultStyleKey = typeof(SLTextBox); } /// <summary> /// 验证 /// </summary> /// <param name="type"></param> /// <param name="errorMsg"></param> public bool Validate(ValidateTypeEmnu type, string errorMsg) { bool r = true; switch (type) { case ValidateTypeEmnu.Email: r = this.Text.IsEmailValid(); //验证EMAIL break; case ValidateTypeEmnu.MustFill: r = !String.IsNullOrEmpty(this.Text.Trim()); //必填项验证 break; case ValidateTypeEmnu.Numeral: r = this.Text.IsNumeric(); //验证数字 break; } if (!r) { this.SetValidation(errorMsg); this.RaiseValidationError(); } return r; } /// <summary> /// OnApplyTemplate /// </summary> public override void OnApplyTemplate() { base.OnApplyTemplate(); mustFillText = GetTemplateChild(PART_MustFillText) as TextBlock; if (mustFillText != null) mustFillText.Visibility = _isMustFill ? Visibility.Visible : Visibility.Collapsed; if (!String.IsNullOrEmpty(_editImageSource)) { editImage = GetTemplateChild(PART_EditImage) as Image; if (editImage != null) editImage.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(_editImageSource, UriKind.Relative)); } } } /// <summary> /// Extensions /// </summary> public static class ValidateExtensions { /// <summary> /// SetValidation /// </summary> /// <param name="frameworkElement"></param> /// <param name="message"></param> public static void SetValidation(this FrameworkElement frameworkElement, string message) { CustomValidation customValidation = new CustomValidation(message); Binding binding = new Binding("ValidationError") { Mode = System.Windows.Data.BindingMode.TwoWay, NotifyOnValidationError = true, ValidatesOnExceptions = true, Source = customValidation }; frameworkElement.SetBinding(Control.TagProperty, binding); } /// <summary> /// RaiseValidationError /// </summary> /// <param name="frameworkElement"></param> public static void RaiseValidationError(this FrameworkElement frameworkElement) { BindingExpression b = frameworkElement.GetBindingExpression(Control.TagProperty); if (b != null) { ((CustomValidation)b.DataItem).ShowErrorMessage = true; b.UpdateSource(); } } /// <summary> /// ClearValidationError /// </summary> /// <param name="frameworkElement"></param> public static void ClearValidationError(this FrameworkElement frameworkElement) { BindingExpression b = frameworkElement.GetBindingExpression(Control.TagProperty); if (b != null) { ((CustomValidation)b.DataItem).ShowErrorMessage = false; b.UpdateSource(); } } /// <summary> /// 判断是否是数字 /// </summary> /// <param name="str">待验证的字符串</param> /// <returns></returns> public static bool IsNumeric(this string str) { return Regex.IsMatch(str, @"^[+-]?/d+(/./d+)?$"); } /// <summary> /// 是否是Email /// </summary> /// <param name="inputEmail"></param> /// <returns></returns> public static bool IsEmailValid(this string inputEmail) { bool isEmailValid = true; string emailExpression = @"^([0-9a-zA-Z]([-./w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-/w]*[0-9a-zA-Z]/.)+[a-zA-Z]{2,9})$"; Regex re = new Regex(emailExpression); if (!re.IsMatch(inputEmail)) { isEmailValid = false; } return isEmailValid; } } /// <summary> /// CustomValidation /// </summary> public class CustomValidation { private string message; public bool ShowErrorMessage { get; set; } public object ValidationError { get { return null; } set { if (ShowErrorMessage) { throw new ValidationException(message); } } } public CustomValidation(string message) { this.message = message; } } 这里是theme <Style TargetType="controls:SLTextBox"> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Background" Value="#FFFFFFFF"/> <Setter Property="Foreground" Value="#FF000000"/> <Setter Property="Padding" Value="2"/> <Setter Property="BorderBrush"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFA3AEB9" Offset="0"/> <GradientStop Color="#FF8399A9" Offset="0.375"/> <GradientStop Color="#FF718597" Offset="0.375"/> <GradientStop Color="#FF617584" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="controls:SLTextBox"> <Grid x:Name="RootElement"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="CommonStates"> <vsm:VisualState x:Name="Normal"/> <vsm:VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" To="#FF99C1E2" Duration="0"/> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="ReadOnly"> <Storyboard> <DoubleAnimation Storyboard.TargetName="ReadOnlyVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0" /> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> <vsm:VisualStateGroup x:Name="FocusStates"> <vsm:VisualState x:Name="Focused"> <Storyboard> <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="EditGlyph" Storyboard.TargetProperty="Opacity"> <SplineDoubleKeyFrame KeyTime="0" Value="1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Unfocused"> <Storyboard> <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> <vsm:VisualStateGroup x:Name="ValidationStates"> <vsm:VisualState x:Name="Valid"/> <vsm:VisualState x:Name="InvalidUnfocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="InvalidFocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <sys:Boolean>True</sys:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"> <Grid> <Border x:Name="ReadOnlyVisualElement" Opacity="0" Background="#5EC9C9C9"/> <Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent"> <ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False"/> </Border> <Image x:Name="EditGlyph" Opacity="0" Margin="0,0,4,0" Height="11" Width="12" HorizontalAlignment="Right" VerticalAlignment="Center" ToolTipService.ToolTip="Click to Edit" /> <TextBlock x:Name="txtMustFill" Visibility="Collapsed" Text="*" HorizontalAlignment="Right" Margin="-10,0" VerticalAlignment="Center" ></TextBlock> </Grid> </Border> <Border x:Name="DisabledVisualElement" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Opacity="0" IsHitTestVisible="False"/> <Border x:Name="FocusVisualElement" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}" Margin="1" Opacity="0" IsHitTestVisible="False"/> <Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="1" BorderBrush="#FFDB000C" Visibility="Collapsed"> <ToolTipService.ToolTip> <ToolTip x:Name="validationTooltip" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <ToolTip.Triggers> <EventTrigger RoutedEvent="Canvas.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <sys:Boolean>true</sys:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </ToolTip.Triggers> </ToolTip> </ToolTipService.ToolTip> <Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent"> <Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C"/> <Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/> </Grid> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <ControlTemplate x:Key="ValidationToolTipTemplate"> <Grid x:Name="Root" Margin="5,0" RenderTransformOrigin="0,0" Opacity="0"> <Grid.RenderTransform> <TranslateTransform x:Name="xform" X="-25"/> </Grid.RenderTransform> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup Name="OpenStates"> <vsm:VisualStateGroup.Transitions> <vsm:VisualTransition GeneratedDuration="0"/> <vsm:VisualTransition To="Open" GeneratedDuration="0:0:0.2"> <Storyboard> <DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2"> <DoubleAnimation.EasingFunction> <BackEase Amplitude=".3" EasingMode="EaseOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/> </Storyboard> </vsm:VisualTransition> </vsm:VisualStateGroup.Transitions> <vsm:VisualState x:Name="Closed"> <Storyboard> <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/> </Storyboard> </vsm:VisualState> <vsm:VisualState x:Name="Open"> <Storyboard> <DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0"/> <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/> </Storyboard> </vsm:VisualState> </vsm:VisualStateGroup> </vsm:VisualStateManager.VisualStateGroups> <Border Margin="4,4,-4,-4" Background="#052A2E31" CornerRadius="5"/> <Border Margin="3,3,-3,-3" Background="#152A2E31" CornerRadius="4"/> <Border Margin="2,2,-2,-2" Background="#252A2E31" CornerRadius="3"/> <Border Margin="1,1,-1,-1" Background="#352A2E31" CornerRadius="2"/> <Border Background="#FFDC000C" CornerRadius="2"/> <Border CornerRadius="2"> <TextBlock UseLayoutRounding="false" Foreground="White" Margin="8,4,8,4" MaxWidth="250" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}"/> </Border> </Grid> </ControlTemplate>