WPF 数据验证

MyTextBox.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Style x:Key="{x:Type TextBoxBase}" TargetType="{x:Type TextBoxBase}" BasedOn="{x:Null}">
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="2,1,1,1"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="SelectionBrush" Value="{DynamicResource Accent}" />
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Border BorderThickness="1" BorderBrush="#FFdc000c" VerticalAlignment="Top">
                            <Grid>
                                <AdornedElementPlaceholder x:Name="adorner" Margin="-1"/>
                            </Grid>
                        </Border>
                        <Border x:Name="errorBorder" Background="#FFdc000c" Margin="8,0,0,0"
                                Opacity="0" CornerRadius="0"
                                IsHitTestVisible="False"
                                MinHeight="24" >
                            <TextBlock Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"
                                       Foreground="White" Margin="8,2,8,3" TextWrapping="Wrap" VerticalAlignment="Center"/>
                        </Border>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <DataTrigger Value="True">
                            <DataTrigger.Binding>
                                <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
                            </DataTrigger.Binding>
                            <DataTrigger.EnterActions>
                                <BeginStoryboard x:Name="fadeInStoryboard">
                                    <Storyboard>
                                        <DoubleAnimation Duration="00:00:00.15"
                                                         Storyboard.TargetName="errorBorder"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="1"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="fadeInStoryboard"/>
                                <BeginStoryboard x:Name="fadeOutStoryBoard">
                                    <Storyboard>
                                        <DoubleAnimation Duration="00:00:00"
                                                         Storyboard.TargetName="errorBorder"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="0"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBoxBase}">
                    <Border x:Name="Bd"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            Background="{TemplateBinding Background}"
                            Padding="{TemplateBinding Padding}"
                            SnapsToDevicePixels="true">
                        <ScrollViewer x:Name="PART_ContentHost" RenderOptions.ClearTypeHint="Enabled"
                                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="{DynamicResource InputTextDisabled}"/>
                        </Trigger>
                        <Trigger Property="IsReadOnly" Value="true">
                            <Setter Property="Foreground" Value="{DynamicResource InputTextDisabled}"/>
                        </Trigger>
                        <Trigger Property="IsFocused" Value="true">
                            <Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource Accent}" />
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsReadOnly" Value="False"/>
                                <Condition Property="IsEnabled" Value="True"/>
                                <Condition Property="IsMouseOver" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background" Value="{DynamicResource InputBackgroundHover}"/>
                            <Setter Property="BorderBrush" Value="{DynamicResource InputBorderHover}"/>
                            <Setter Property="Foreground" Value="{DynamicResource InputTextHover}"/>
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style BasedOn="{StaticResource {x:Type TextBoxBase}}" TargetType="{x:Type TextBox}">
    </Style>
    <Style BasedOn="{StaticResource {x:Type TextBoxBase}}" TargetType="{x:Type RichTextBox}">
    </Style>

</ResourceDictionary>

App.xaml 加载:

<Application.Resources>
    <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Template/MyTextBox.xaml"/>
            </ResourceDictionary.MergedDictionaries>     
    </ResourceDictionary>
</Application.Resources>

ValidateModelBase.cs 基类

public class ValidateModelBase : ObservableObject, IDataErrorInfo
{
    public ValidateModelBase()
    {
    }

    #region 属性 

    /// <summary>
    /// 表当验证错误集合
    /// </summary>
    private Dictionary<String, String> dataErrors = new Dictionary<String, String>();

    /// <summary>
    /// 是否验证通过
    /// </summary>
    public Boolean IsValidated
    {
        get
        {
            if (dataErrors != null && dataErrors.Count > 0)
            {
                return false;
            }
            return true;
        }
    }
    #endregion

    public string this[string columnName]
    {
        get
        {
            ValidationContext vc = new ValidationContext(this, null, null);
            vc.MemberName = columnName;
            var res = new List<ValidationResult>();
            var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
            if (res.Count > 0)
            {
                AddDic(dataErrors, vc.MemberName);
                return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
            }
            RemoveDic(dataErrors, vc.MemberName);
            return null;
        }
    }

    public string Error
    {
        get
        {
            return null;
        }
    }


    #region 附属方法

    /// <summary>
    /// 移除字典
    /// </summary>
    /// <param name="dics"></param>
    /// <param name="dicKey"></param>
    private void RemoveDic(Dictionary<String, String> dics, String dicKey)
    {
        dics.Remove(dicKey);
    }

    /// <summary>
    /// 添加字典
    /// </summary>
    /// <param name="dics"></param>
    /// <param name="dicKey"></param>
    private void AddDic(Dictionary<String, String> dics, String dicKey)
    {
        if (!dics.ContainsKey(dicKey)) dics.Add(dicKey, "");
    }

    #endregion
}

VM 继承:

[MetadataType(typeof(UserInfo))]
public class UserInfo: ValidateModelBase
{
    #region 属性 

    private String userName;
    /// <summary>
    /// 用户名
    /// </summary>
    [Required(ErrorMessage = "姓名是必须的")]
    public String UserName
    {
        get { return userName; }
        set { userName = value; RaisePropertyChanged(() => UserName); }
    }

    private String userPhone;
    /// <summary>
    /// 用户电话
    /// </summary>
    [Required(ErrorMessage ="电话是必须的")]
    [RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用户电话必须为8-11位的数值.")]
    public String UserPhone
    {
        get { return userPhone; }
        set { userPhone = value; RaisePropertyChanged(() => UserPhone); }
    }

    private String userEmail ;
    /// <summary>
    /// 用户邮件
    /// </summary>
    [Required(ErrorMessage ="邮件是必须的")]
    [StringLength(100, MinimumLength = 2)]
    [RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "请填写正确的邮箱地址.")]
    public String UserEmail
    {
        get { return userEmail; }
        set { userEmail = value; RaisePropertyChanged(() => UserEmail); }
    }

    private int _age;

    [Required(ErrorMessage ="请填入年龄")]
    //[Range(0.0, 100.0, ErrorMessage = "数据超出范围")]
    [Range(double.NegativeInfinity, 100.0, ErrorMessage = "年龄需小于 100")]  // NegativeInfinity 负无穷
    public int Age
    {
        get { return _age; }
        set { _age = value; RaisePropertyChanged(nameof(Age)); }
    }

    private string _note = "无";
    public string Note
    {
        get { return _note; }
        set { _note = value; RaisePropertyChanged(nameof(Note)); }
    }

    #endregion
}

页面绑定:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen"
        DataContext="{Binding Source={StaticResource Locator}, Path=UserInfoModel}"
        Title="MainWindow" Height="300" Width="500">
    <Grid>
        <GroupBox Header="数据验证测试" Margin="10" >
            <StackPanel  Margin="10">

                <StackPanel.Resources>
                </StackPanel.Resources>

                <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
                    <TextBlock Text="用户名" Width="100"/>
                    <TextBox Width="150" Text="{Binding User.UserName, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
                    <TextBlock Text="用户邮箱" Width="100"/>
                    <TextBox Width="150" Text="{Binding User.UserEmail, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
                    <TextBlock Text="用户电话" Width="100"/>
                    <TextBox Width="150" Text="{Binding User.UserPhone, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
                    <TextBlock Text="用户年龄" Width="100"/>
                    <TextBox Width="150" Text="{Binding User.Age, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0 5 0 0">
                    <TextBlock Text="备注" Width="100"/>
                    <TextBox Width="150" Text="{Binding User.Note, UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/>
                </StackPanel>                

                <Button Content="提交" Width="150" Margin="100 10 0 0" HorizontalAlignment="Left" Command="{Binding SubmitCmd}"/>
            </StackPanel>
        </GroupBox>
    </Grid>
</Window>




参考:

https://www.cnblogs.com/wzh2010/p/6518834.html

posted @ 2022-11-07 21:56  double64  阅读(136)  评论(0编辑  收藏  举报