WPF開發隨筆收錄-DataAnnotations實現數據校驗(MVVM架構下)


一、前言

在自己的項目中挺多地方需要涉及到數據驗證的,初期的實現方式都是通過點擊確定后再逐個驗證數據是否符合要求,但這種方式會讓后台代碼變得很多很亂。於是就開始在網上需求好的解決方式,剛好看到了一個大佬的博客寫了關於數據驗證的博客,也成功將那個方法用在項目中了,今天就來這里分享一下。博客原文:https://www.cnblogs.com/wzh2010/p/6518834.html

二、正文

1、這里寫一個簡單的例子來作為參考,首先新建個項目,按照常規MVVM構建好項目,如下圖。然后添加需要用到的通知基類和命令基類

 

 2、然后就是添加一個驗證的基類ValidateModelBase,后面需要實現驗證的類直接繼承這個類即可,這個代碼是直接搬大佬的

public class ValidateModelBase : NotifyBase, IDataErrorInfo
{
    public ValidateModelBase()
    {

    }

    #region 屬性 
    /// <summary>
    /// 表當驗證錯誤集合
    /// </summary>
    public Dictionary<string, string> dataErrors = new Dictionary<string, string>();

    /// <summary>
    /// 是否驗證通過
    /// </summary>
    public bool 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)
            {
                string errorInfo = string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                AddDic(dataErrors, columnName, errorInfo);
                return errorInfo;
            }
            RemoveDic(dataErrors, columnName);
            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, string dicValue)
    {
        if (!dics.ContainsKey(dicKey)) dics.Add(dicKey, dicValue);
    }
    #endregion
}

3、接着我們創建一個類,並添加需要驗證的字段

public class UserModel : ValidateModelBase
{
    private string userName;
    [Required(ErrorMessage = "用戶名不可為空")]
    public string UserName
    {
        get { return userName; }
        set { userName = value; DoNotify(); }
    }

    private string userAge;
    [Required(ErrorMessage = "年齡不可為空")]
    [Range(0, 100, ErrorMessage = "年齡范圍為0~100")]
    public string UserAge
    {
        get { return userAge; }
        set { userAge = value; DoNotify(); }
    }

    private bool isFormValid;

    public bool IsFormValid
    {
        get { return isFormValid; }
        set { isFormValid = value; DoNotify(); }
    }
}

4、重寫一下TextBox的Validation.ErrorTemplate模板

<Style
    x:Key="{x:Type TextBoxBase}"
    BasedOn="{x:Null}"
    TargetType="{x:Type TextBoxBase}">
    <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="Vertical">
                    <Border
                        x:Name="adornerborder"
                        BorderThickness="1">
                        <Grid>
                            <AdornedElementPlaceholder x:Name="adorner" Margin="-1" />
                        </Grid>
                    </Border>
                    <Border
                        x:Name="errorBorder"
                        Background="Transparent"
                        CornerRadius="0"
                        IsHitTestVisible="False"
                        Opacity="0">
                        <TextBlock
                            VerticalAlignment="Center"
                            Foreground="Red"
                            Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
                    </Border>
                </StackPanel>

                <ControlTemplate.Triggers>
                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <Binding ElementName="adorner" Path="AdornedElement.Tag" />
                        </DataTrigger.Binding>
                        <DataTrigger.EnterActions>
                            <BeginStoryboard x:Name="fadeInStoryboard1">
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="errorBorder"
                                        Storyboard.TargetProperty="Opacity"
                                        To="1"
                                        Duration="00:00:00.15" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.Setters>
                            <Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
                        </DataTrigger.Setters>
                    </DataTrigger>

                    <DataTrigger Value="True">
                        <DataTrigger.Binding>
                            <Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
                        </DataTrigger.Binding>
                        <DataTrigger.EnterActions>
                            <BeginStoryboard x:Name="fadeInStoryboard">
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetName="errorBorder"
                                        Storyboard.TargetProperty="Opacity"
                                        To="1"
                                        Duration="00:00:00.15" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.Setters>
                            <Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
                        </DataTrigger.Setters>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

5、接着在主窗口添加兩個TextBox,然后綁定上對應的字段,記得要加上這個ValidatesOnDataErrors=True,不然無效

<Grid>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel Margin="0,10" Orientation="Horizontal">
            <Label
                Width="80"
                Content="用戶姓名:" />
            <TextBox
                Width="150"
                Tag="{Binding UserModel.IsFormValid, UpdateSourceTrigger=PropertyChanged}"
                Text="{Binding UserModel.UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
        </StackPanel>
        <StackPanel Margin="0,10" Orientation="Horizontal">
            <Label
                Width="80"
                Content="用戶年齡:" />
            <TextBox
                Width="150"
                Tag="{Binding UserModel.IsFormValid, UpdateSourceTrigger=PropertyChanged}"
                Text="{Binding UserModel.UserAge, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
        </StackPanel>

        <Button
            Margin="0,10,0,0"
            Command="{Binding SubmitCommand}"
            Content="提交" />
    </StackPanel>
</Grid>

6、然后就是ViewModel的代碼

public class MainWindowViewModel : NotifyBase
{
    private UserModel userModel;

    public UserModel UserModel
    {
        get { return userModel; }
        set { userModel = value; DoNotify(); }
    }

    public CommandBase SubmitCommand { get; set; } = new CommandBase();

    public MainWindowViewModel()
    {
        UserModel = new UserModel();

        SubmitCommand.DoExecute = new Action<object>(DoSumit);
        SubmitCommand.DoCanExecute = new Func<object, bool>((o) => { return true; });
    }

    private void DoSumit(object obj)
    {
        if (UserModel.IsValidated) MessageBox.Show("驗證通過!");
        else
        {
            UserModel.IsFormValid = true;
            var dataErrors = UserModel.dataErrors;
            MessageBox.Show("驗證失敗");
        }
    }
}

7、到這里功能的實現就基本完成了,不擅長講解原理,不太懂得可以研究研究官方文檔獲取其他大佬的文章,實現效果如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM