您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“MVVMLight怎么綁定在表單驗證上”,內容詳細,步驟清晰,細節處理妥當,希望這篇“MVVMLight怎么綁定在表單驗證上”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
表單驗證是MVVM體系中的重要一塊。而綁定除了推動 Model-View-ViewModel (MVVM) 模式松散耦合 邏輯、數據 和 UI定義 的關系之外,還為業務數據驗證方案提供強大而靈活的支持。
WPF 中的數據綁定機制包括多個選項,可用于在創建可編輯視圖時校驗輸入數據的有效性。
驗證類型 | 說明 |
Exception 驗證 | 通過在某個 Binding 對象上設置 ValidatesOnExceptions 屬性,如果源對象屬性設置已修改的值的過程中引發異常,則拋出錯誤并為該 Binding 設置驗證錯誤。 |
ValidationRule 驗證 | Binding 類具有一個用于提供 ValidationRule 派生類實例的集合的屬性。這些 ValidationRules 需要覆蓋某個 Validate 方法,該方法由 Binding 在每次綁定控件中的數據發生更改時進行調用。 如果 Validate 方法返回無效的 ValidationResult 對象,則將為該 Binding 設置驗證錯誤。 |
IDataErrorInfo 驗證 | 通過在綁定數據源對象上實現 IDataErrorInfo 接口并在 Binding 對象上設置 ValidatesOnDataErrors 屬性,Binding 將調用從綁定數據源對象公開的 IDataErrorInfo API。 如果從這些屬性調用返回非 null 或非空字符串,則將為該 Binding 設置驗證錯誤。 |
我們在使用 WPF 中的數據綁定來呈現業務數據時,通常會使用 Binding 對象在目標控件的單個屬性與數據源對象屬性之間提供數據管道。
如果要使得綁定驗證有效,首先需要進行 TwoWay 數據綁定。這表明,除了從源屬性流向目標屬性以進行顯示的數據之外,編輯過的數據也會從目標流向源。
這就是偉大的雙向數據綁定的精髓,所以在MVVM中做數據校驗,會容易的多。
當 TwoWay 數據綁定中輸入或修改數據時,將啟動以下工作流:
1、 | 用戶通過鍵盤、鼠標、手寫板或者其他輸入設備來輸入或修改數據,從而改變綁定的目標信息 |
2、 | 設置源屬性值。 |
3、 | 觸發 Binding.SourceUpdated 事件。 |
4、 | 如果數據源屬性上的 setter 引發異常,則異常會由 Binding 捕獲,并可用于指示驗證錯誤。 |
5、 | 如果實現了 IDataErrorInfo 接口,則會對數據源對象調用該接口的方法獲得該屬性的錯誤信息。 |
6、 | 向用戶呈現驗證錯誤指示,并觸發 Validation.Error 附加事件。 |
綁定目標向綁定源發送數據更新的請求,而綁定源則對數據進行驗證,并根據不同的驗證機制進行反饋。
下面我們用實例來對比下這幾種驗證機制,在此之前,我們先做一個事情,就是寫一個錯誤觸發的樣式,來保證錯誤觸發的時候直接清晰的向用戶反饋出去。
我們新建一個資源字典文件,命名為TextBox.xaml,下面這個是資源字典文件的內容,目標類型是TextBoxBase基礎的控件,如TextBox和RichTextBox.
代碼比較簡單,注意標紅的內容,設計一個紅底白字的提示框,當源屬性觸發錯誤驗證的時候,把驗證對象集合中的錯誤內容顯示出來。
<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 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>
達到的效果如下:
正如說明中描述的那樣,在具有綁定關系的源字段模型上做驗證異常的引發并拋出,在View中的Xaml對象上設置 ExceptionValidationRule 屬性,響應捕獲異常并顯示。
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("該字段不能為空!"); } } }
結果如圖:
將驗證失敗的信息直接拋出來,這無疑是最簡單粗暴的,實現也很簡單,但是只是針對單一源屬性進行驗證, 復用性不高。
而且在組合驗證(比如同時需要驗證非空和其他規則)情況下,會導致Model中寫過重過臃腫的代碼。
通過繼承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, "郵箱地址不準確!"); } } return new ValidationResult(true, null); }
創建了兩個類,一個用于驗證是否為空,一個用于驗證是否符合郵箱地址標準格式。
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
結果如下:
說明:相對來說,這種方式是比較不錯的,獨立性、復用性都很好,從松散耦合角度來說也是比較恰當的。
可以預先寫好一系列的驗證規則類,視圖編碼人員可以根據需求直接使用這些驗證規則,服務端無需額外的處理。
但是仍然有缺點,擴展性差,如果需要個性化反饋消息也需要額外擴展。不符合日益豐富的前端驗證需求。
3.1、在綁定數據源對象上實現 IDataErrorInfo 接口
3.2、在 Binding 對象上設置 ValidatesOnDataErrors 屬性
Binding 將調用從綁定數據源對象公開的 IDataErrorInfo API。如果從這些屬性調用返回非 null 或非空字符串,則將為該 Binding 設置驗證錯誤。
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位的數值!"; } } if (columnName == "UserEmail" && !String.IsNullOrEmpty(this.UserEmail)) { if (!emailReg.IsMatch(this.UserEmail.ToString())) { return "用戶郵箱地址不正確!"; } } return null; } } }
繼承IDataErrorInfo接口后,實現方法兩個屬性:Error 屬性用于指示整個對象的錯誤,而索引器用于指示單個屬性級別的錯誤。
每次的屬性值發生變化,則索引器進行一次檢查,看是否有驗證錯誤的信息返回。
兩者的工作原理相同:如果返回非 null 或非空字符串,則表示存在驗證錯誤。否則,返回的字符串用于向用戶顯示錯誤。
結果如圖:
利用 IDataErrorInfo 的好處是它可用于輕松地處理交叉耦合屬性。但也具有一個很大的弊端:
索引器的實現通常會導致較大的 switch-case 語句(對象中的每個屬性名稱都對應于一種情況),
必須基于字符串進行切換和匹配,并返回指示錯誤的字符串。而且,在對象上設置屬性值之前,不會調用 IDataErrorInfo 的實現。
為了避免出現大量的 switch-case,并且將校驗邏輯進行分離提高代碼復用,將驗證規則和驗證信息獨立化于于每個模型對象中, 使用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位的數值.")] 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 = "請填寫正確的郵箱地址.")] 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相信很多人很熟悉,可以使用數據批注來自定義用戶的模型數據,記得引用 System.ComponentModel.DataAnnotations。
他包含如下幾個驗證類型:
驗證屬性 | 說明 |
CustomValidationAttribute | 使用自定義方法進行驗證。 |
DataTypeAttribute | 指定特定類型的數據,如電子郵件地址或電話號碼。 |
EnumDataTypeAttribute | 確保值存在于枚舉中。 |
RangeAttribute | 指定最小和最大約束。 |
RegularExpressionAttribute | 使用正則表達式來確定有效的值。 |
RequiredAttribute | 指定必須提供一個值。 |
StringLengthAttribute | 指定最大和最小字符數。 |
ValidationAttribute | 用作驗證屬性的基類。 |
這邊我們使用到了RequiredAttribute、StringLengthAttribute、RegularExpressionAttribute 三項,如果有需要進一步了解 DataAnnotations 的可以參考微軟官網:
https://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx
用 DataAnnotions 后,Model 的更加簡潔,校驗也更加靈活。可以疊加組合驗證 , 面對復雜驗證模式的時候,可以自由的使用正則來驗證。
默認情況下,框架會提供相應需要反饋的消息內容,當然也可以自定義錯誤消息內容:ErrorMessage 。
這邊我們還加了個全局的錯誤集合收集器 :dataErrors,在提交判斷時候判斷是否驗證通過。
這邊我們進一步封裝索引器,并且通過反射技術讀取當前字段下的屬性進行驗證。
結果如下:
封裝ValidateModelBase類:
上面的驗證比較合理了,不過相對于開發人員還是太累贅了,開發人員關心的是Model的DataAnnotations的配置,而不是關心在這個ViewModel要如何做驗證處理,所以我們進一步抽象。
編寫一個ValidateModelBase,把需要處理的工作都放在里面。需要驗證屬性的Model去繼承這個基類。如下:
ValidateModelBase 類,請注意標紅部分:
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 }
驗證的模型類:繼承 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位的數值.")] 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 = "請填寫正確的郵箱地址.")] 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 }
結果如下:
讀到這里,這篇“MVVMLight怎么綁定在表單驗證上”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。