欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MVVMLight項目之綁定在表單驗證上的應(yīng)用示例分析

 更新時間:2022年01月31日 14:17:34   作者:Brand  
這篇文章主要為大家介紹了MVVMLight項目中綁定在表單驗證上的應(yīng)用示例及源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步除夕快樂,新年快樂

表單驗證是MVVM體系中的重要一塊。而綁定除了推動 Model-View-ViewModel (MVVM) 模式松散耦合 邏輯、數(shù)據(jù) 和 UI定義 的關(guān)系之外,還為業(yè)務(wù)數(shù)據(jù)驗證方案提供強大而靈活的支持。

WPF 中的數(shù)據(jù)綁定機制包括多個選項,可用于在創(chuàng)建可編輯視圖時校驗輸入數(shù)據(jù)的有效性。

常見的表單驗證機制有如下幾種:

驗證類型說明
Exception 驗證通過在某個 Binding 對象上設(shè)置 ValidatesOnExceptions 屬性,如果源對象屬性設(shè)置已修改的值的過程中引發(fā)異常,則拋出錯誤并為該 Binding 設(shè)置驗證錯誤。
ValidationRule 驗證

Binding 類具有一個用于提供 ValidationRule 派生類實例的集合的屬性。這些 ValidationRules 需要覆蓋某個 Validate 方法,該方法由 Binding 在每次綁定控件中的數(shù)據(jù)發(fā)生更改時進行調(diào)用。

如果 Validate 方法返回?zé)o效的 ValidationResult 對象,則將為該 Binding 設(shè)置驗證錯誤。

IDataErrorInfo 驗證

通過在綁定數(shù)據(jù)源對象上實現(xiàn) IDataErrorInfo 接口并在 Binding 對象上設(shè)置 ValidatesOnDataErrors 屬性,Binding 將調(diào)用從綁定數(shù)據(jù)源對象公開的 IDataErrorInfo API。

如果從這些屬性調(diào)用返回非 null 或非空字符串,則將為該 Binding 設(shè)置驗證錯誤。

驗證交互的關(guān)系模式如圖:

我們在使用 WPF 中的數(shù)據(jù)綁定來呈現(xiàn)業(yè)務(wù)數(shù)據(jù)時,通常會使用 Binding 對象在目標(biāo)控件的單個屬性與數(shù)據(jù)源對象屬性之間提供數(shù)據(jù)管道。

如果要使得綁定驗證有效,首先需要進行 TwoWay 數(shù)據(jù)綁定。這表明,除了從源屬性流向目標(biāo)屬性以進行顯示的數(shù)據(jù)之外,編輯過的數(shù)據(jù)也會從目標(biāo)流向源。

這就是偉大的雙向數(shù)據(jù)綁定的精髓,所以在MVVM中做數(shù)據(jù)校驗,會容易的多。

當(dāng) TwoWay 數(shù)據(jù)綁定中輸入或修改數(shù)據(jù)時,將啟動以下工作流:

1、 用戶通過鍵盤、鼠標(biāo)、手寫板或者其他輸入設(shè)備來輸入或修改數(shù)據(jù),從而改變綁定的目標(biāo)信息
2、設(shè)置源屬性值。
3、觸發(fā) Binding.SourceUpdated 事件。
4、如果數(shù)據(jù)源屬性上的 setter 引發(fā)異常,則異常會由 Binding 捕獲,并可用于指示驗證錯誤。
5、如果實現(xiàn)了 IDataErrorInfo 接口,則會對數(shù)據(jù)源對象調(diào)用該接口的方法獲得該屬性的錯誤信息。
6、向用戶呈現(xiàn)驗證錯誤指示,并觸發(fā) Validation.Error 附加事件。

綁定目標(biāo)向綁定源發(fā)送數(shù)據(jù)更新的請求,而綁定源則對數(shù)據(jù)進行驗證,并根據(jù)不同的驗證機制進行反饋。 

下面我們用實例來對比下這幾種驗證機制,在此之前,我們先做一個事情,就是寫一個錯誤觸發(fā)的樣式,來保證錯誤觸發(fā)的時候直接清晰的向用戶反饋出去。

我們新建一個資源字典文件,命名為TextBox.xaml,下面這個是資源字典文件的內(nèi)容,目標(biāo)類型是TextBoxBase基礎(chǔ)的控件,如TextBox和RichTextBox.

代碼比較簡單,注意標(biāo)紅的內(nèi)容,設(shè)計一個紅底白字的提示框,當(dāng)源屬性觸發(fā)錯誤驗證的時候,把驗證對象集合中的錯誤內(nèi)容顯示出來。

<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中全局注冊到整個應(yīng)用中。

 <Application x:Class="MVVMLightDemo.App" 
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
              StartupUri="View/BindingFormView.xaml" 
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              d1p1:Ignorable="d" 
              xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:vm="clr-namespace:MVVMLightDemo.ViewModel"
              xmlns:Common="clr-namespace:MVVMLightDemo.Common">
   <Application.Resources>
     <ResourceDictionary>
             <ResourceDictionary.MergedDictionaries>
                 <ResourceDictionary Source="/MVVMLightDemo;component/Assets/TextBox.xaml" />
             </ResourceDictionary.MergedDictionaries>
             <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
             <Common:IntegerToSex x:Key="IntegerToSex" d:IsDataSource="True" />
     </ResourceDictionary>
   </Application.Resources>
</Application>

 達到的效果如下:

下面詳細描述下這三種驗證模式  

1、Exception 驗證:

正如說明中描述的那樣,在具有綁定關(guān)系的源字段模型上做驗證異常的引發(fā)并拋出,在View中的Xaml對象上設(shè)置 ExceptionValidationRule 屬性,響應(yīng)捕獲異常并顯示。

View代碼:

                <GroupBox Header="Exception 驗證" Margin="10 10 10 10" DataContext="{Binding Source={StaticResource Locator},Path=ValidateException}" >
                     <StackPanel x:Name="ExceptionPanel" Orientation="Vertical" Margin="0,10,0,0" >
                         <StackPanel>
                             <Label Content="用戶名" Target="{Binding ElementName=UserNameEx}"/>
                             <TextBox x:Name="UserNameEx" Width="150">
                                 <TextBox.Text>
                                     <Binding Path="UserNameEx" UpdateSourceTrigger="PropertyChanged">
                                         <Binding.ValidationRules>
                                             <ExceptionValidationRule></ExceptionValidationRule>
                                         </Binding.ValidationRules>
                                     </Binding>
                                 </TextBox.Text>
                             </TextBox>
                         </StackPanel>
                     </StackPanel>
                 </GroupBox>

  ViewModel代碼:

   /// <summary>
    /// Exception 驗證
    /// </summary>
    public class ValidateExceptionViewModel:ViewModelBase
    {
        public ValidateExceptionViewModel()
        {
        }
        private String userNameEx;
        /// <summary>
        /// 用戶名稱(不為空)
        /// </summary>
        public string UserNameEx
        {
            get
            {
                return userNameEx;
            }
            set
            {
                userNameEx = value;
                RaisePropertyChanged(() => UserNameEx);
                if (string.IsNullOrEmpty(value))
                {
                    throw new ApplicationException("該字段不能為空!");
                }
            }
        }
    

  結(jié)果如圖:

將驗證失敗的信息直接拋出來,這無疑是最簡單粗暴的,實現(xiàn)也很簡單,但是只是針對單一源屬性進行驗證, 復(fù)用性不高。

而且在組合驗證(比如同時需要驗證非空和其他規(guī)則)情況下,會導(dǎo)致Model中寫過重過臃腫的代碼。

2、ValidationRule 驗證:

通過繼承ValidationRule 抽象類,并重寫他的Validate方法來擴展編寫我們需要的驗證類。該驗證類可以直接使用在我們需要驗證的屬性。

View代碼:

 <GroupBox Header="ValidationRule 驗證"  Margin="10 20 10 10" DataContext="{Binding Source={StaticResource Locator},Path=ValidationRule}" >
   <StackPanel x:Name="ValidationRulePanel" Orientation="Vertical" Margin="0,20,0,0">
       <StackPanel>
           <Label Content="用戶名" Target="{Binding ElementName=UserName}"/>
           <TextBox Width="150" >
               <TextBox.Text>
                   <Binding Path="UserName" UpdateSourceTrigger="PropertyChanged">
                   <Binding.ValidationRules>
                       <app:RequiredRule />
                   </Binding.ValidationRules>
               </Binding>
               </TextBox.Text>
           </TextBox>
       </StackPanel>
       <StackPanel>
           <Label Content="用戶郵箱" Target="{Binding ElementName=UserEmail}"/>
           <TextBox Width="150">
               <TextBox.Text>
                   <Binding Path="UserEmail" UpdateSourceTrigger="PropertyChanged">
                       <Binding.ValidationRules>
                           <app:EmailRule />
                       </Binding.ValidationRules>
                   </Binding>
               </TextBox.Text>
           </TextBox>
       </StackPanel>
   </StackPanel>
  </GroupBox>

 重寫兩個ValidationRule,代碼如下:

public class RequiredRule : ValidationRule
 {
     public override ValidationResult Validate(object value, CultureInfo cultureInfo)
     {
         if (value == null)
             return new ValidationResult(false, "該字段不能為空值!");
         if (string.IsNullOrEmpty(value.ToString()))
             return new ValidationResult(false, "該字段不能為空字符串!");
         return new ValidationResult(true, null);
     }
 }
 public class EmailRule : ValidationRule
 {
     public override ValidationResult Validate(object value, CultureInfo cultureInfo)
     {
         Regex emailReg = new Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$");

         if (!String.IsNullOrEmpty(value.ToString()))
         {
             if (!emailReg.IsMatch(value.ToString()))
             {
                 return new ValidationResult(false, "郵箱地址不準(zhǔn)確!");
             }
         }
         return new ValidationResult(true, null);
     }

 創(chuàng)建了兩個類,一個用于驗證是否為空,一個用于驗證是否符合郵箱地址標(biāo)準(zhǔn)格式。 

ViewModel代碼:

 public class ValidationRuleViewModel:ViewModelBase
    {
        public ValidationRuleViewModel()
        {
        }
        #region 屬性
        private String userName;
        /// <summary>
        /// 用戶名
        /// </summary>
        public String UserName
        {
            get { return userName; }
            set { userName = value; RaisePropertyChanged(()=>UserName); }
        }
        private String userEmail;
        /// <summary>
        /// 用戶郵件
        /// </summary>
        public String UserEmail
        {
            get { return userEmail; }
            set { userEmail = value;RaisePropertyChanged(()=>UserName);  }
        }
        #endregion

 結(jié)果如下:

 說明:相對來說,這種方式是比較不錯的,獨立性、復(fù)用性都很好,從松散耦合角度來說也是比較恰當(dāng)?shù)摹?/p>

可以預(yù)先寫好一系列的驗證規(guī)則類,視圖編碼人員可以根據(jù)需求直接使用這些驗證規(guī)則,服務(wù)端無需額外的處理。

但是仍然有缺點,擴展性差,如果需要個性化反饋消息也需要額外擴展。不符合日益豐富的前端驗證需求。

3、IDataErrorInfo 驗證:

3.1、在綁定數(shù)據(jù)源對象上實現(xiàn) IDataErrorInfo 接口

3.2、在 Binding 對象上設(shè)置 ValidatesOnDataErrors 屬性

Binding 將調(diào)用從綁定數(shù)據(jù)源對象公開的 IDataErrorInfo API。如果從這些屬性調(diào)用返回非 null 或非空字符串,則將為該 Binding 設(shè)置驗證錯誤。

View代碼:

      <GroupBox Header="IDataErrorInfo 驗證" Margin="10 20 10 10" DataContext="{Binding Source={StaticResource Locator},Path=BindingForm}" >
           <StackPanel x:Name="Form" Orientation="Vertical" Margin="0,20,0,0">
               <StackPanel>
                   <Label Content="用戶名" Target="{Binding ElementName=UserName}"/>
                   <TextBox Width="150" 
                        Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
                   </TextBox>
               </StackPanel>

               <StackPanel>
                   <Label Content="性別" Target="{Binding ElementName=RadioGendeMale}"/>
                   <RadioButton Content="男" />
                   <RadioButton Content="女" Margin="8,0,0,0" />
               </StackPanel>
               <StackPanel>
                   <Label Content="生日" Target="{Binding ElementName=DateBirth}" />
                   <DatePicker x:Name="DateBirth" />
               </StackPanel>
               <StackPanel>
                   <Label Content="用戶郵箱" Target="{Binding ElementName=UserEmail}"/>
                   <TextBox Width="150" Text="{Binding UserEmail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
               </StackPanel>
               <StackPanel>
                   <Label Content="用戶電話" Target="{Binding ElementName=UserPhone}"/>
                   <TextBox Width="150" Text="{Binding UserPhone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
               </StackPanel>
           </StackPanel>
       </GroupBox>

 ViewModel代碼:

public class BindingFormViewModel :ViewModelBase, IDataErrorInfo
   {
       public BindingFormViewModel()
       {
       }
       #region 屬性
       private String userName;
       /// <summary>
       /// 用戶名
       /// </summary>
       public String UserName
       {
           get { return userName; }
           set { userName = value; }
       }
       private String userPhone;
       /// <summary>
       /// 用戶電話
       /// </summary>
       public String UserPhone
       {
           get { return userPhone; }
           set { userPhone = value; }
       }
       private String userEmail;
       /// <summary>
       /// 用戶郵件
       /// </summary>
       public String UserEmail
       {
           get { return userEmail; }
           set { userEmail = value; }
       }
       #endregion
       public String Error
       {
           get { return null; }
       }
       public String this[string columnName]
       {
           get
           {
               Regex digitalReg = new Regex(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$");
               Regex emailReg = new Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$");
               if (columnName == "UserName" && String.IsNullOrEmpty(this.UserName))
               {
                   return "用戶名不能為空";
               }
               
               if (columnName == "UserPhone" && !String.IsNullOrEmpty(this.UserPhone))
               {
                   if (!digitalReg.IsMatch(this.UserPhone.ToString()))
                   {
                       return "用戶電話必須為8-11位的數(shù)值!";
                   }
               }
               if (columnName == "UserEmail" && !String.IsNullOrEmpty(this.UserEmail))
               {
                   if (!emailReg.IsMatch(this.UserEmail.ToString()))
                   {
                       return "用戶郵箱地址不正確!";
                   }
               }
               return null;
           }
       }
   }

繼承IDataErrorInfo接口后,實現(xiàn)方法兩個屬性:Error 屬性用于指示整個對象的錯誤,而索引器用于指示單個屬性級別的錯誤。

每次的屬性值發(fā)生變化,則索引器進行一次檢查,看是否有驗證錯誤的信息返回。

兩者的工作原理相同:如果返回非 null 或非空字符串,則表示存在驗證錯誤。否則,返回的字符串用于向用戶顯示錯誤。 

結(jié)果如圖:

 利用 IDataErrorInfo 的好處是它可用于輕松地處理交叉耦合屬性。但也具有一個很大的弊端:
索引器的實現(xiàn)通常會導(dǎo)致較大的 switch-case 語句(對象中的每個屬性名稱都對應(yīng)于一種情況),
必須基于字符串進行切換和匹配,并返回指示錯誤的字符串。而且,在對象上設(shè)置屬性值之前,不會調(diào)用 IDataErrorInfo 的實現(xiàn)。

為了避免出現(xiàn)大量的 switch-case,并且將校驗邏輯進行分離提高代碼復(fù)用,將驗證規(guī)則和驗證信息獨立化于于每個模型對象中, 使用DataAnnotations 無疑是最好的的方案 。

所以我們進行改良一下:

View代碼,跟上面那個一樣:

<GroupBox Header="IDataErrorInfo+ 驗證" Margin="10 20 10 10" DataContext="{Binding Source={StaticResource Locator},Path=BindDataAnnotations}" >
         <StackPanel Orientation="Vertical" Margin="0,20,0,0">
             <StackPanel>
                 <Label Content="用戶名" Target="{Binding ElementName=UserName}"/>
                 <TextBox Width="150" 
                      Text="{Binding UserName,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}" >
                 </TextBox>
             </StackPanel>
             <StackPanel>
                 <Label Content="性別" Target="{Binding ElementName=RadioGendeMale}"/>
                 <RadioButton Content="男" />
                 <RadioButton Content="女" Margin="8,0,0,0" />
             </StackPanel>
             <StackPanel>
                 <Label Content="生日" Target="{Binding ElementName=DateBirth}" />
                 <DatePicker />
             </StackPanel>
             <StackPanel>
                 <Label Content="用戶郵箱" Target="{Binding ElementName=UserEmail}"/>
                 <TextBox Width="150" Text="{Binding UserEmail, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
             </StackPanel>
             <StackPanel>
                 <Label Content="用戶電話" Target="{Binding ElementName=UserPhone}"/>
                 <TextBox Width="150" Text="{Binding UserPhone,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
             </StackPanel>
             <Button Content="提交" Margin="100,16,0,0" HorizontalAlignment="Left" Command="{Binding ValidFormCommand}" />
         </StackPanel>

</GroupBox>

 VideModel代碼:

using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using GalaSoft.MvvmLight.Command;
using System.Windows;

namespace MVVMLightDemo.ViewModel
{
    [MetadataType(typeof(BindDataAnnotationsViewModel))]
    public class BindDataAnnotationsViewModel : ViewModelBase, IDataErrorInfo
    {
        public BindDataAnnotationsViewModel()
        {    
        }
        #region 屬性 
        /// <summary>
        /// 表單驗證錯誤集合
        /// </summary>
        private Dictionary<String, String> dataErrors = new Dictionary<String, String>();
        private String userName;
        /// <summary>
        /// 用戶名
        /// </summary>
        [Required]
        public String UserName
        {
            get { return userName; }
            set { userName = value; }
        }
        private String userPhone;
        /// <summary>
        /// 用戶電話
        /// </summary>
        [Required]
        [RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用戶電話必須為8-11位的數(shù)值.")]
        public String UserPhone
        {
            get { return userPhone; }
            set { userPhone = value; }
        }
        private String userEmail;
        /// <summary>
        /// 用戶郵件
        /// </summary>
        [Required]
        [StringLength(100,MinimumLength=2)]
        [RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "請?zhí)顚懻_的郵箱地址.")]
        public String UserEmail
        {
            get { return userEmail; }
            set { userEmail = value; }
        }
        #endregion
        #region 命令
        private RelayCommand validFormCommand;
        /// <summary>
        /// 驗證表單
        /// </summary>
        public RelayCommand ValidFormCommand
        {
            get
            {
                if (validFormCommand == null)
                    return new RelayCommand(() => ExcuteValidForm());
                return validFormCommand;
            }
            set { validFormCommand = value; }
        }
        /// <summary>
        /// 驗證表單
        /// </summary>
        private void ExcuteValidForm()
        {
            if (dataErrors.Count == 0) MessageBox.Show("驗證通過!");
            else MessageBox.Show("驗證失?。?);
        }
        #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
    }
}

  DataAnnotations相信很多人很熟悉,可以使用數(shù)據(jù)批注來自定義用戶的模型數(shù)據(jù),記得引用 System.ComponentModel.DataAnnotations。

他包含如下幾個驗證類型: 

驗證屬性說明 
CustomValidationAttribute使用自定義方法進行驗證。
DataTypeAttribute指定特定類型的數(shù)據(jù),如電子郵件地址或電話號碼。
EnumDataTypeAttribute確保值存在于枚舉中。
RangeAttribute指定最小和最大約束。
RegularExpressionAttribute使用正則表達式來確定有效的值。
RequiredAttribute指定必須提供一個值。
StringLengthAttribute指定最大和最小字符數(shù)。
ValidationAttribute用作驗證屬性的基類。

    這邊我們使用到了RequiredAttribute、StringLengthAttribute、RegularExpressionAttribute 三項,如果有需要進一步了解 DataAnnotations 的可以參考微軟官網(wǎng):

https://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx

用 DataAnnotions 后,Model 的更加簡潔,校驗也更加靈活??梢辕B加組合驗證 , 面對復(fù)雜驗證模式的時候,可以自由的使用正則來驗證。

默認(rèn)情況下,框架會提供相應(yīng)需要反饋的消息內(nèi)容,當(dāng)然也可以自定義錯誤消息內(nèi)容:ErrorMessage 。

這邊我們還加了個全局的錯誤集合收集器 :dataErrors,在提交判斷時候判斷是否驗證通過。

這邊我們進一步封裝索引器,并且通過反射技術(shù)讀取當(dāng)前字段下的屬性進行驗證。

 結(jié)果如下:

封裝ValidateModelBase類:

上面的驗證比較合理了,不過相對于開發(fā)人員還是太累贅了,開發(fā)人員關(guān)心的是Model的DataAnnotations的配置,而不是關(guān)心在這個ViewModel要如何做驗證處理,所以我們進一步抽象。

編寫一個ValidateModelBase,把需要處理的工作都放在里面。需要驗證屬性的Model去繼承這個基類。如下:

ValidateModelBase 類,請注意標(biāo)紅部分:

 public class ValidateModelBase : ObservableObject, IDataErrorInfo
     {
         public ValidateModelBase()
         {
               
         }
         #region 屬性 
         /// <summary>
         /// 表當(dāng)驗證錯誤集合
         /// </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
     }

 驗證的模型類:繼承 ValidateModelBase

[MetadataType(typeof(BindDataAnnotationsViewModel))]
    public class ValidateUserInfo : ValidateModelBase
    {
        #region 屬性 
        private String userName;
        /// <summary>
        /// 用戶名
        /// </summary>
        [Required]
        public String UserName
        {
            get { return userName; }
            set { userName = value; RaisePropertyChanged(() => UserName); }
        }
        private String userPhone;
        /// <summary>
        /// 用戶電話
        /// </summary>
        [Required]
        [RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用戶電話必須為8-11位的數(shù)值.")]
        public String UserPhone
        {
            get { return userPhone; }
            set { userPhone = value; RaisePropertyChanged(() => UserPhone); }
        }
        private String userEmail;
        /// <summary>
        /// 用戶郵件
        /// </summary>
        [Required]
        [StringLength(100, MinimumLength = 2)]
        [RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "請?zhí)顚懻_的郵箱地址.")]
        public String UserEmail
        {
            get { return userEmail; }
            set { userEmail = value; RaisePropertyChanged(() => UserEmail);  }
        }
        #endregion
    }

  ViewModel代碼如下:

public class PackagedValidateViewModel:ViewModelBase
   {
       public PackagedValidateViewModel()
       {
           ValidateUI = new Model.ValidateUserInfo();
       }

       #region 全局屬性
       private ValidateUserInfo validateUI;
       /// <summary>
       /// 用戶信息
       /// </summary>
       public ValidateUserInfo ValidateUI
       {
           get
           {
               return validateUI;
           }

           set
           {
               validateUI = value;
               RaisePropertyChanged(()=>ValidateUI);
           }
       }             
       #endregion
       #region 全局命令
       private RelayCommand submitCmd;
       public RelayCommand SubmitCmd
       {
           get
           {
               if(submitCmd == null) return new RelayCommand(() => ExcuteValidForm());
               return submitCmd;
           }

           set
           {
               submitCmd = value;
           }
       }
       #endregion
       #region 附屬方法
       /// <summary>
       /// 驗證表單
       /// </summary>
       private void ExcuteValidForm()
       {
           if (ValidateUI.IsValidated) MessageBox.Show("驗證通過!");
           else MessageBox.Show("驗證失敗!");
       }
       #endregion
   }

 結(jié)果如下:

以上就是MVVMLight項目之綁定在表單驗證上的應(yīng)用示例分析的詳細內(nèi)容,更多關(guān)于MVVMLight綁定在表單驗證上的應(yīng)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android使用MediaPlayer和TextureView實現(xiàn)視頻無縫切換

    Android使用MediaPlayer和TextureView實現(xiàn)視頻無縫切換

    這篇文章主要為大家詳細介紹了Android使用MediaPlayer和TextureView實現(xiàn)視頻無縫切換,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • Android Apt之Activity Route的示例

    Android Apt之Activity Route的示例

    本篇文章主要介紹了Android Apt之Activity Route的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • Android ActionBar制作時鐘實例解析

    Android ActionBar制作時鐘實例解析

    這篇文章主要為大家詳細介紹了Android ActionBar制作時鐘的實現(xiàn)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • AndroidStudio:手勢識別

    AndroidStudio:手勢識別

    這篇文章主要介紹了AndroidStudio手勢識別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Android 中ListView點擊Item無響應(yīng)問題的解決辦法

    Android 中ListView點擊Item無響應(yīng)問題的解決辦法

    如果listitem里面包括button或者checkbox等控件,默認(rèn)情況下listitem會失去焦點,導(dǎo)致無法響應(yīng)item的事件,怎么解決呢?下面小編給大家分享下listview點擊item無響應(yīng)的解決辦法
    2016-12-12
  • Android圖片或拍照選擇圖片功能實例代碼

    Android圖片或拍照選擇圖片功能實例代碼

    這篇文章主要給大家介紹了關(guān)于Android圖片或拍照選擇圖片功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • 玩轉(zhuǎn)Android之Drawable的使用

    玩轉(zhuǎn)Android之Drawable的使用

    這篇文章主要為大家詳細介紹了Android之Drawable的使用方法,幫助大家系統(tǒng)的學(xué)習(xí)一下Drawable的使用,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Android利用Paint自定義View實現(xiàn)進度條控件方法示例

    Android利用Paint自定義View實現(xiàn)進度條控件方法示例

    這篇文章主要給大家介紹了關(guān)于Android利用Paint自定義View實現(xiàn)進度條控件的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Android開發(fā)MQTT協(xié)議的模型及通信淺析

    Android開發(fā)MQTT協(xié)議的模型及通信淺析

    這篇文章主要W為大家介紹了Android開發(fā)MQTT協(xié)議的模型及通信淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Android中文件讀寫(輸入流和輸出流)操作小結(jié)

    Android中文件讀寫(輸入流和輸出流)操作小結(jié)

    這篇文章主要介紹了Android中文件讀寫(輸入流和輸出流)操作小結(jié),本文總結(jié)了Android中文件讀寫的原理、字節(jié)流和字符流的區(qū)別、文件讀寫的步驟、輸入流和輸出流以及代碼實例等內(nèi)容,需要的朋友可以參考下
    2015-06-06

最新評論