亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

常見(jiàn)的表單驗(yàn)證機(jī)制有如下幾種:

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

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

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

IDataErrorInfo 驗(yàn)證

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

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

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

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

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

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

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

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

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

下面我們用實(shí)例來(lái)對(duì)比下這幾種驗(yàn)證機(jī)制,在此之前,我們先做一個(gè)事情,就是寫(xiě)一個(gè)錯(cuò)誤觸發(fā)的樣式,來(lái)保證錯(cuò)誤觸發(fā)的時(shí)候直接清晰的向用戶(hù)反饋出去。

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

代碼比較簡(jiǎn)單,注意標(biāo)紅的內(nèi)容,設(shè)計(jì)一個(gè)紅底白字的提示框,當(dāng)源屬性觸發(fā)錯(cuò)誤驗(yàn)證的時(shí)候,把驗(yàn)證對(duì)象集合中的錯(cuò)誤內(nèi)容顯示出來(lá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中全局注冊(cè)到整個(gè)應(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>

 達(dá)到的效果如下:

下面詳細(xì)描述下這三種驗(yàn)證模式  

1、Exception 驗(yàn)證:

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

View代碼:

                <GroupBox Header="Exception 驗(yàn)證" 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="用戶(hù)名" 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 驗(yàn)證
    /// </summary>
    public class ValidateExceptionViewModel:ViewModelBase
    {
        public ValidateExceptionViewModel()
        {
        }
        private String userNameEx;
        /// <summary>
        /// 用戶(hù)名稱(chēng)(不為空)
        /// </summary>
        public string UserNameEx
        {
            get
            {
                return userNameEx;
            }
            set
            {
                userNameEx = value;
                RaisePropertyChanged(() => UserNameEx);
                if (string.IsNullOrEmpty(value))
                {
                    throw new ApplicationException("該字段不能為空!");
                }
            }
        }
    

  結(jié)果如圖:

將驗(yàn)證失敗的信息直接拋出來(lái),這無(wú)疑是最簡(jiǎn)單粗暴的,實(shí)現(xiàn)也很簡(jiǎn)單,但是只是針對(duì)單一源屬性進(jìn)行驗(yàn)證, 復(fù)用性不高。

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

2、ValidationRule 驗(yàn)證:

通過(guò)繼承ValidationRule 抽象類(lèi),并重寫(xiě)他的Validate方法來(lái)擴(kuò)展編寫(xiě)我們需要的驗(yàn)證類(lèi)。該驗(yàn)證類(lèi)可以直接使用在我們需要驗(yàn)證的屬性。

View代碼:

 <GroupBox Header="ValidationRule 驗(yàn)證"  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="用戶(hù)名" 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="用戶(hù)郵箱" 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>

 重寫(xiě)兩個(gè)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)建了兩個(gè)類(lèi),一個(gè)用于驗(yàn)證是否為空,一個(gè)用于驗(yàn)證是否符合郵箱地址標(biāo)準(zhǔn)格式。 

ViewModel代碼:

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

 結(jié)果如下:

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

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

但是仍然有缺點(diǎn),擴(kuò)展性差,如果需要個(gè)性化反饋消息也需要額外擴(kuò)展。不符合日益豐富的前端驗(yàn)證需求。

3、IDataErrorInfo 驗(yàn)證:

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

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

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

View代碼:

      <GroupBox Header="IDataErrorInfo 驗(yàn)證" 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="用戶(hù)名" 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="用戶(hù)郵箱" Target="{Binding ElementName=UserEmail}"/>
                   <TextBox Width="150" Text="{Binding UserEmail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
               </StackPanel>
               <StackPanel>
                   <Label Content="用戶(hù)電話" 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>
       /// 用戶(hù)名
       /// </summary>
       public String UserName
       {
           get { return userName; }
           set { userName = value; }
       }
       private String userPhone;
       /// <summary>
       /// 用戶(hù)電話
       /// </summary>
       public String UserPhone
       {
           get { return userPhone; }
           set { userPhone = value; }
       }
       private String userEmail;
       /// <summary>
       /// 用戶(hù)郵件
       /// </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 "用戶(hù)名不能為空";
               }
               
               if (columnName == "UserPhone" && !String.IsNullOrEmpty(this.UserPhone))
               {
                   if (!digitalReg.IsMatch(this.UserPhone.ToString()))
                   {
                       return "用戶(hù)電話必須為8-11位的數(shù)值!";
                   }
               }
               if (columnName == "UserEmail" && !String.IsNullOrEmpty(this.UserEmail))
               {
                   if (!emailReg.IsMatch(this.UserEmail.ToString()))
                   {
                       return "用戶(hù)郵箱地址不正確!";
                   }
               }
               return null;
           }
       }
   }

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

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

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

結(jié)果如圖:

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

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

所以我們進(jìn)行改良一下:

View代碼,跟上面那個(gè)一樣:

<GroupBox Header="IDataErrorInfo+ 驗(yàn)證" Margin="10 20 10 10" DataContext="{Binding Source={StaticResource Locator},Path=BindDataAnnotations}" >
         <StackPanel Orientation="Vertical" Margin="0,20,0,0">
             <StackPanel>
                 <Label Content="用戶(hù)名" 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="用戶(hù)郵箱" Target="{Binding ElementName=UserEmail}"/>
                 <TextBox Width="150" Text="{Binding UserEmail, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
             </StackPanel>
             <StackPanel>
                 <Label Content="用戶(hù)電話" 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>
        /// 表單驗(yàn)證錯(cuò)誤集合
        /// </summary>
        private Dictionary<String, String> dataErrors = new Dictionary<String, String>();
        private String userName;
        /// <summary>
        /// 用戶(hù)名
        /// </summary>
        [Required]
        public String UserName
        {
            get { return userName; }
            set { userName = value; }
        }
        private String userPhone;
        /// <summary>
        /// 用戶(hù)電話
        /// </summary>
        [Required]
        [RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用戶(hù)電話必須為8-11位的數(shù)值.")]
        public String UserPhone
        {
            get { return userPhone; }
            set { userPhone = value; }
        }
        private String userEmail;
        /// <summary>
        /// 用戶(hù)郵件
        /// </summary>
        [Required]
        [StringLength(100,MinimumLength=2)]
        [RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "請(qǐng)?zhí)顚?xiě)正確的郵箱地址.")]
        public String UserEmail
        {
            get { return userEmail; }
            set { userEmail = value; }
        }
        #endregion
        #region 命令
        private RelayCommand validFormCommand;
        /// <summary>
        /// 驗(yàn)證表單
        /// </summary>
        public RelayCommand ValidFormCommand
        {
            get
            {
                if (validFormCommand == null)
                    return new RelayCommand(() => ExcuteValidForm());
                return validFormCommand;
            }
            set { validFormCommand = value; }
        }
        /// <summary>
        /// 驗(yàn)證表單
        /// </summary>
        private void ExcuteValidForm()
        {
            if (dataErrors.Count == 0) MessageBox.Show("驗(yàn)證通過(guò)!");
            else MessageBox.Show("驗(yàn)證失??!");
        }
        #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ù)批注來(lái)自定義用戶(hù)的模型數(shù)據(jù),記得引用 System.ComponentModel.DataAnnotations。

他包含如下幾個(gè)驗(yàn)證類(lèi)型: 

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

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

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

用 DataAnnotions 后,Model 的更加簡(jiǎn)潔,校驗(yàn)也更加靈活??梢辕B加組合驗(yàn)證 , 面對(duì)復(fù)雜驗(yàn)證模式的時(shí)候,可以自由的使用正則來(lái)驗(yàn)證。

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

這邊我們還加了個(gè)全局的錯(cuò)誤集合收集器 :dataErrors,在提交判斷時(shí)候判斷是否驗(yàn)證通過(guò)。

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

 結(jié)果如下:

封裝ValidateModelBase類(lèi):

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

編寫(xiě)一個(gè)ValidateModelBase,把需要處理的工作都放在里面。需要驗(yàn)證屬性的Model去繼承這個(gè)基類(lèi)。如下:

ValidateModelBase 類(lèi),請(qǐng)注意標(biāo)紅部分:

 public class ValidateModelBase : ObservableObject, IDataErrorInfo
     {
         public ValidateModelBase()
         {
               
         }
         #region 屬性 
         /// <summary>
         /// 表當(dāng)驗(yàn)證錯(cuò)誤集合
         /// </summary>
         private Dictionary<String, String> dataErrors = new Dictionary<String, String>();
 
         /// <summary>
         /// 是否驗(yàn)證通過(guò)
         /// </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
     }

 驗(yàn)證的模型類(lèi):繼承 ValidateModelBase

[MetadataType(typeof(BindDataAnnotationsViewModel))]
    public class ValidateUserInfo : ValidateModelBase
    {
        #region 屬性 
        private String userName;
        /// <summary>
        /// 用戶(hù)名
        /// </summary>
        [Required]
        public String UserName
        {
            get { return userName; }
            set { userName = value; RaisePropertyChanged(() => UserName); }
        }
        private String userPhone;
        /// <summary>
        /// 用戶(hù)電話
        /// </summary>
        [Required]
        [RegularExpression(@"^[-]?[1-9]{8,11}\d*$|^[0]{1}$", ErrorMessage = "用戶(hù)電話必須為8-11位的數(shù)值.")]
        public String UserPhone
        {
            get { return userPhone; }
            set { userPhone = value; RaisePropertyChanged(() => UserPhone); }
        }
        private String userEmail;
        /// <summary>
        /// 用戶(hù)郵件
        /// </summary>
        [Required]
        [StringLength(100, MinimumLength = 2)]
        [RegularExpression("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$", ErrorMessage = "請(qǐng)?zhí)顚?xiě)正確的郵箱地址.")]
        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>
       /// 用戶(hù)信息
       /// </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>
       /// 驗(yàn)證表單
       /// </summary>
       private void ExcuteValidForm()
       {
           if (ValidateUI.IsValidated) MessageBox.Show("驗(yàn)證通過(guò)!");
           else MessageBox.Show("驗(yàn)證失??!");
       }
       #endregion
   }

 結(jié)果如下:

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

相關(guān)文章

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

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

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

    Android Apt之Activity Route的示例

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

    Android ActionBar制作時(shí)鐘實(shí)例解析

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

    AndroidStudio:手勢(shì)識(shí)別

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

    Android 中ListView點(diǎn)擊Item無(wú)響應(yīng)問(wèn)題的解決辦法

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

    Android圖片或拍照選擇圖片功能實(shí)例代碼

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

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

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

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

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

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

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

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

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

最新評(píng)論