zoukankan      html  css  js  c++  java
  • [Silverlight入门系列]使用MVVM模式(5):异步Validation数据验证和INotifyDataErrorInfo接口

    数据验证(Validation)是界面程序的常见需求,例如使用正则表达式验证用户输入的Email地址是否合法,然后在界面给出错误提示信息。在Sivlerlight的MVVM模式中,我们在Model和ViewModel可以做Validation,然后需要把Model和ViewModel的Validation结果和错误信息通知视图(View)。在WPF中,我们使用IDataErrorInfo,在Silverlight4中,建议使用INotifyDataErrorInfo。关于这个接口怎么使用,如何实现ErrorProvider的功能,如何做DataForm的Validatio,请参考我的旧一篇

    IDataErrorInfo

    先简单说一下IDataErrorInfo,这个接口实现了简单的数据验证和错误报告功能,只能说聊胜于无吧。例子:

    1 <TextBox Text="{Binding Path=CurrentEmployee.Name, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True }" />

    INotifyDataErrorInfo

    这个接口只有Silverlight4以上支持,非常强大,支持一个绑定属性多重错误、异步数据验证、自动通知视图错误信息、ErrorChanged事件、HasErrors属性、GetErrors方法等等。定义:

    1 public interface INotifyDataErrorInfo
    2 {
    3 bool HasErrors { get; }
    4
    5 event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    6
    7 IEnumerable GetErrors(string propertyName);
    8 }

    实现这个INotifyDataErrorInfo接口也非常简单,来个简单的例子:

    1 public class SimpleModel : INotifyDataErrorInfo
    2 {
    3 public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    4
    5 private Dictionary<string, List<String>> _errors = new Dictionary<string, List<String>>();
    6
    7 private string _accountID = null;
    8
    9 public string AccountID
    10 {
    11 get { return _accountID; }
    12 set
    13 {
    14 if (_accountID != value)
    15 {
    16 var propertyName = "AccountID";
    17
    18 if (string.IsNullOrEmpty(value))
    19 {
    20 if (!_errors.ContainsKey(propertyName))
    21 _errors.Add(propertyName, new List<string>());
    22
    23 _errors[propertyName].Add("AccountID can't be null or empty");
    24 }
    25 else
    26 {
    27 if (_errors.ContainsKey(propertyName))
    28 _errors.Remove(propertyName);
    29 }
    30
    31 NotifyErrorsChanged(propertyName);
    32
    33 //Maybe you don't want to set this field to a value if the validation fails
    34   _accountID = value;
    35 }
    36 }
    37
    38 }
    39
    40 public System.Collections.IEnumerable GetErrors(string propertyName)
    41 {
    42 if (_errors.ContainsKey(propertyName))
    43 return _errors[propertyName];
    44
    45 return _errors.Values;
    46 }
    47
    48 public bool HasErrors
    49 {
    50 get { return _errors.Count > 0; }
    51 }
    52
    53
    54 private void NotifyErrorsChanged(string propertyName)
    55 {
    56 if (ErrorsChanged != null)
    57 ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    58 }
    59 }

    异步Validation数据验证和INotifyDataErrorInfo接口

    这个例子稍微复杂,实现了异步调用WCF RIA Service进行业务逻辑的validation并在ViewModel中把验证的错误提示通知视图,完整的代码下载,需要VS2010和Silverlight环境。

    代码说明 ViewModel基类:

    1 using System;
    2 using System.Net;
    3 using System.Windows;
    4 using System.Linq;
    5 using System.Windows.Controls;
    6 using System.Windows.Documents;
    7 using System.Windows.Ink;
    8 using System.Windows.Input;
    9 using System.Windows.Media;
    10 using System.Windows.Media.Animation;
    11 using System.Windows.Shapes;
    12 using System.ComponentModel;
    13 using System.Collections.Generic;
    14 using System.Collections;
    15
    16 namespace AsycValidation
    17 {
    18 public class BasicViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
    19 {
    20 public event PropertyChangedEventHandler PropertyChanged;
    21 public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    22
    23
    24 private Dictionary<string, List<ValidationErrorInfo>> _errors =
    25 new Dictionary<string, List<ValidationErrorInfo>>();
    26
    27
    28 protected void RemoveErrorFromPropertyAndNotifyErrorChanges(
    29 string propertyName,
    30 int errorCode)
    31 {
    32 if (_errors.ContainsKey(propertyName))
    33 {
    34 RemoveErrorFromPropertyIfErrorCodeAlreadyExist(propertyName, errorCode);
    35
    36 NotifyErrorsChanged(propertyName);
    37 }
    38 }
    39
    40 private void RemoveErrorFromPropertyIfErrorCodeAlreadyExist(
    41 string propertyName,
    42 int errorCode)
    43 {
    44 if (_errors.ContainsKey(propertyName))
    45 {
    46 var errorToRemove = _errors[propertyName].SingleOrDefault(
    47 error => error.ErrorCode == errorCode);
    48
    49 if (errorToRemove != null)
    50 {
    51 _errors[propertyName].Remove(errorToRemove);
    52
    53
    54
    55
    56 if (_errors[propertyName].Count == 0)
    57 _errors.Remove(propertyName);
    58 }
    59 }
    60 }
    61 protected void AddErrorToPropertyAndNotifyErrorChanges(
    62 string propertyName,
    63 ValidationErrorInfo errorInfo)
    64 {
    65 RemoveErrorFromPropertyIfErrorCodeAlreadyExist(propertyName, errorInfo.ErrorCode);
    66 if (!_errors.ContainsKey(propertyName))
    67 _errors.Add(propertyName, new List<ValidationErrorInfo>());
    68
    69 _errors[propertyName].Add(errorInfo);
    70
    71 NotifyErrorsChanged(propertyName);
    72 }
    73
    74
    75 public IEnumerable GetErrors(string propertyName)
    76 {
    77 if (!_errors.ContainsKey(propertyName))
    78 return _errors.Values;
    79
    80 return _errors[propertyName];
    81 }
    82
    83
    84 public bool HasErrors
    85 {
    86 get { return this._errors.Count > 0; }
    87 }
    88
    89
    90 private void NotifyErrorsChanged(string propertyName)
    91 {
    92 if (ErrorsChanged != null)
    93 ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    94 }
    95
    96
    97 protected void NotifyPropertyChanged(string propertyName)
    98 {
    99 if (PropertyChanged != null)
    100 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    101 }
    102
    103 }
    104 }

    Model:

    1 using System;
    2 using System.Net;
    3 using System.Windows;
    4 using System.Windows.Controls;
    5 using System.Windows.Documents;
    6 using System.Windows.Ink;
    7 using System.Windows.Input;
    8 using System.Windows.Media;
    9 using System.Windows.Media.Animation;
    10 using System.Windows.Shapes;
    11 using System.ComponentModel;
    12
    13 namespace AsycValidation
    14 {
    15 public class CompanyModel : INotifyPropertyChanged
    16 {
    17 public event PropertyChangedEventHandler PropertyChanged;
    18
    19 public int CompanyID { get; set; }
    20
    21 private string _CompanyName;
    22 public string CompanyName
    23 {
    24 get { return _CompanyName; }
    25 set
    26 {
    27 _CompanyName = value;
    28
    29 if (PropertyChanged != null)
    30 {
    31 PropertyChanged(this, new PropertyChangedEventArgs("CompanyName"));
    32 }
    33 }
    34 }
    35 }
    36 }

    ViewModel,继承了BaseViewModel基类:

    1 using System;
    2 using System.Net;
    3 using System.Windows;
    4 using System.Windows.Controls;
    5 using System.Windows.Documents;
    6 using System.Windows.Ink;
    7 using System.Windows.Input;
    8 using System.Windows.Media;
    9 using System.Windows.Media.Animation;
    10 using System.Windows.Shapes;
    11 using AsycValidation.Web;
    12
    13 namespace AsycValidation
    14 {
    15 public class CompanyViewModel : BasicViewModel
    16 {
    17 public CompanyModel CompanyModelData { get; set; }
    18
    19 public CompanyViewModel(CompanyModel model)
    20 {
    21 CompanyModelData = model;
    22 }
    23
    24 private string _CompanyName = null;
    25 private const int ACCOUNT_ALREADY_EXIST_ERROCODE = 100;
    26
    27 DomainService1 service = new DomainService1();
    28
    29 public string CompanyName
    30 {
    31 get
    32 {
    33 return _CompanyName;
    34 }
    35 set
    36 {
    37 if (_CompanyName != value)
    38 {
    39 var propertyName = "CompanyName";
    40
    41 ValidateAccountAlreadyExists(
    42 value,
    43 propertyName,
    44 ACCOUNT_ALREADY_EXIST_ERROCODE,
    45 string.Format("Company with the ID {0} already exists", value));
    46
    47 _CompanyName = value;
    48 NotifyPropertyChanged(propertyName);
    49 }
    50 }
    51 }
    52
    53 private void ValidateAccountAlreadyExists(
    54 string CompanyID,
    55 string propertyName,
    56 int errorCode,
    57 string errorMsg)
    58 {
    59 service.DoesCompanyExists(
    60 CompanyID,
    61 invokeOperation =>
    62 {
    63 if (invokeOperation.Value)
    64 {
    65 AddErrorToPropertyAndNotifyErrorChanges(
    66 propertyName,
    67 new ValidationErrorInfo()
    68 {
    69 ErrorCode = errorCode,
    70 ErrorMessage = errorMsg
    71 });
    72 }
    73 else
    74 {
    75 RemoveErrorFromPropertyAndNotifyErrorChanges(
    76 propertyName,
    77 errorCode);
    78 }
    79 },
    80 null);
    81 }
    82
    83 }
    84 }

    View / XAML

    1 <UserControl x:Class="AsycValidation.MainPage"
    2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6 xmlns:wm="clr-namespace:AsycValidation"
    7 mc:Ignorable="d"
    8 d:DesignHeight="300" d:DesignWidth="400">
    9
    10 <Grid Name="Layout" >
    11 <TextBlock Height="32" HorizontalAlignment="Left" Margin="41,53,0,0" Name="textBlock1" Text="Company:" VerticalAlignment="Top" Width="66" />
    12 <TextBox Height="31" HorizontalAlignment="Left" Margin="120,45,0,0" Name="textBox1" Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=True}" VerticalAlignment="Top" Width="119" />
    13 <TextBox Height="30" HorizontalAlignment="Left" Margin="120,104,0,0" Name="textBox2" VerticalAlignment="Top" Width="119" />
    14 <Button Content="Button" Height="36" HorizontalAlignment="Left" Margin="120,156,0,0" Name="button1" VerticalAlignment="Top" Width="81" />
    15 </Grid>
    16 </UserControl>

    XAML.CS

    1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Net;
    5 using System.Windows;
    6 using System.Windows.Controls;
    7 using System.Windows.Documents;
    8 using System.Windows.Input;
    9 using System.Windows.Media;
    10 using System.Windows.Media.Animation;
    11 using System.Windows.Shapes;
    12
    13 namespace AsycValidation
    14 {
    15 public partial class MainPage : UserControl
    16 {
    17 public MainPage()
    18 {
    19 InitializeComponent();
    20
    21 CompanyModel m1 = new CompanyModel() { CompanyID = 1, CompanyName = "abc" };
    22
    23 companyViewModel = new CompanyViewModel(m1);
    24 this.DataContext = companyViewModel;
    25 }
    26
    27 public CompanyViewModel companyViewModel { get; set; }
    28 }
    29 }

    WCF Ria Service:

    1 namespace AsycValidation.Web
    2 {
    3 using System;
    4 using System.Collections.Generic;
    5 using System.ComponentModel;
    6 using System.ComponentModel.DataAnnotations;
    7 using System.Linq;
    8 using System.ServiceModel.DomainServices.Hosting;
    9 using System.ServiceModel.DomainServices.Server;
    10
    11
    12 // TODO: Create methods containing your application logic.
    13 [EnableClientAccess()]
    14 public class DomainService1 : DomainService
    15 {
    16 [Invoke]
    17 public bool DoesCompanyExists(string companyID)
    18 {
    19 if (companyID == "12345")
    20 return true;
    21
    22 return false;
    23 }
    24 }
    25 }

    这个例子稍微复杂,实现了异步调用WCF RIA Service进行业务逻辑的validation并在ViewModel中把验证的错误提示通知视图,完整的代码下载,需要VS2010和Silverlight环境。

    主要示例了MVVM的INotifyDataErrorInfo接口和INotifyPropertyChanged接口,异步Validation, WCF Ria Service调用。

    Powered By D&J (URL:http://www.cnblogs.com/Areas/)
  • 相关阅读:
    nullptr 与 null
    nullptr 与 null
    UNREFERENCED_PARAMETER 的作用
    Kali-Linux无线网络渗透测试-李亚伟-第3章-监听WiFi网络--虚拟机使用无线网卡
    探寻Linux背后的“美丽心灵”——Linux创始人Linus Torvalds访谈录
    Angular2(Beta)入门
    angular2 will be kiiled by react in the future?
    《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)前言
    Angular vs React – so which do I chose?
    2015-2016前端知识体系
  • 原文地址:https://www.cnblogs.com/Areas/p/2169878.html
Copyright © 2011-2022 走看看