博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[索引页]
[源码下载]


稳扎稳打Silverlight(51) - 4.0绑定之数据验证IDataErrorInfo, INotifyDataErrorInfo



作者:webabcd


介绍
Silverlight 4.0 数据验证:

  • IDataErrorInfo - 对数据实体类提供自定义验证支持。.NET Framework 也有此接口,可以方便移植 
  • INotifyDataErrorInfo - 对数据实体类提供自定义验证支持,比 IDataErrorInfo 功能更强大。INotifyDataErrorInfo 支持异步验证,这就意味着其可以通过验证方法调用 Web 服务和用回调方法更新错误集合来添加服务器端验证 



在线DEMO
http://www.cnblogs.com/webabcd/archive/2010/08/09/1795417.html


示例
1、演示 IDataErrorInfo 的应用
IDataErrorInfoModel.cs

代码
/*
 * IDataErrorInfo - 对数据实体类提供自定义验证支持。.NET Framework 也有此接口,可以方便移植
 *     string Error - 获取对象的验证错误信息
 *     string this[string columnName] - 获取对象的指定字段的验证错误信息
 
*/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Silverlight40.Binding
{
    
public class IDataErrorInfoModel : System.ComponentModel.IDataErrorInfo
    {
        
// 验证错误的提示信息
        private const string ID_ERROR = "id 不能小于 10";
        
private const string NAME_ERROR = "name 不能包含空格";
        
private const string NAME_WARNING = "name 不能小于 5 个字符";

        
// 用于保存验证错误信息。key 保存所验证的字段名称;value 保存对应的字段的验证错误信息列表
        private Dictionary<String, List<String>> errors = new Dictionary<string, List<string>>();

        
private int _id;
        [Display(Name 
= "标识")]
        
public int Id
        {
            
get { return _id; }
            
set 
            {
                
if (value > 1000)
                    
throw new Exception("太大了");

                
if (IsIdValid(value) && _id != value) 
                    _id 
= value; 
            }
        }

        
private string _name;
        [Display(Name 
= "名称")]
        
public string Name
        {
            
get { return _name; }
            
set 
            { 
                
if (IsNameValid(value) && _name != value) 
                    _name 
= value; 
            }
        }

        
public bool IsIdValid(int value)
        {
            
bool isValid = true;

            
if (value < 10)
            {
                AddError(
"Id", ID_ERROR);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Id", ID_ERROR);
            }

            
return isValid;
        }

        
public bool IsNameValid(string value)
        {
            
bool isValid = true;

            
if (value.Contains(" "))
            {
                AddError(
"Name", NAME_ERROR);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Name", NAME_ERROR);
            }

            
if (value.Length < 5)
            {
                AddError(
"Name", NAME_WARNING);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Name", NAME_WARNING);
            }

            
return isValid;
        }
      
        
public void AddError(string propertyName, string error)
        {
            
if (!errors.ContainsKey(propertyName))
                errors[propertyName] 
= new List<string>();

            
if (!errors[propertyName].Contains(error))
                errors[propertyName].Add(error);
        }

        
public void RemoveError(string propertyName, string error)
        {
            
if (errors.ContainsKey(propertyName) && errors[propertyName].Contains(error))
            {
                errors[propertyName].Remove(error);

                
if (errors[propertyName].Count == 0
                    errors.Remove(propertyName);
            }
        }



        
public string Error
        {
            
get { return errors.Count > 0 ? "有验证错误" : "没有验证错误"; }
        }

        
public string this[string propertyName]
        {
            
get 
            {
                
if (errors.ContainsKey(propertyName))
                    
return string.Join(Environment.NewLine, errors[propertyName]);
                
else
                    
return null;
            }
        }
    }
}


IDataErrorInfo.xaml

代码
<navigation:Page x:Class="Silverlight40.Binding.IDataErrorInfo" 
           xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
           xmlns:navigation
="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:sdk
="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
           Title
="IDataErrorInfo Page">
    
<Grid x:Name="LayoutRoot">
        
<StackPanel HorizontalAlignment="Left">

            
<sdk:ValidationSummary Margin="3">
                
<sdk:ValidationSummary.Header>
                    错误列表:
                
</sdk:ValidationSummary.Header>
            
</sdk:ValidationSummary>

            
<!--
                ValidatesOnExceptions - 指定绑定引擎是否报告验证过程中的异常信息
                ValidatesOnDataErrors - 指定绑定引擎是否报告绑定数据实体上的 IDataErrorInfo 所实现的验证错误信息(通过 IDataErrorInfo 的 this[string columnName] 获取验证错误信息)
                NotifyOnValidationError - 当出现验证错误时是否触发 BindingValidationError 事件
            
-->
            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Target="{Binding ElementName=txtId}" />
                
<TextBox Name="txtId" Text="{Binding Id, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" KeyDown="txtId_KeyDown"/>
                
<sdk:DescriptionViewer Description="id 不能小于 10"/>
            
</StackPanel>

            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Target="{Binding ElementName=txtName}"/>
                
<TextBox Name="txtName" Text="{Binding Name, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" KeyDown="txtName_KeyDown"/>
                
<sdk:DescriptionViewer Description="name 不能包含空格且 name 不能小于 5 个字符"/>
            
</StackPanel>
            
            
<Button Name="btnSubmit" Content="获取验证信息" Click="btnSubmit_Click" />
            
        
</StackPanel>
    
</Grid>
</navigation:Page>


IDataErrorInfo.xaml.cs

代码
/*
 * 通过绑定实现了 IDataErrorInfo 接口的实体类来实现自定义数据验证功能
 
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;

namespace Silverlight40.Binding
{
    
public partial class IDataErrorInfo : Page
    {
        IDataErrorInfoModel _model;

        
public IDataErrorInfo()
        {
            InitializeComponent();
        }

        
protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _model 
= new IDataErrorInfoModel() { Id = 100, Name = "webabcd" };
            LayoutRoot.DataContext 
= _model;

            
// BindingValidationError - 当有数据验证错误时所触发的事件。绑定时需要设置 NotifyOnValidationError=True
            txtId.BindingValidationError += (x, y) => { MessageBox.Show(y.Error.ErrorContent.ToString()); };
            txtName.BindingValidationError 
+= (x, y) => { MessageBox.Show(y.Error.ErrorContent.ToString()); };
        }

        
private void txtId_KeyDown(object sender, KeyEventArgs e)
        {
            
// 注:
            
// BindingValidationError 事件只有在控件失去焦点才会被触发
            
// 如果需要在控件没失去焦点的情况下产生验证效果,那么可以通过调用 BindingExpression.UpdateSource() 方法来实现

            
// FrameworkElement.GetBindingExpression(DependencyProperty dp) - 获取 FrameworkElement 的指定属性上的绑定信息
            
// BindingExpression.UpdateSource() - 将当前绑定信息立即发送到绑定方式为 TwoWay 的属性上
            if (e.Key == System.Windows.Input.Key.Enter)
                txtId.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }

        
private void txtName_KeyDown(object sender, KeyEventArgs e)
        {
            
if (e.Key == System.Windows.Input.Key.Enter)
                txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }

        
private void btnSubmit_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(
"验证信息:" + _model.Error);
        }
    }
}



2、演示 INotifyDataErrorInfo 的应用
INotifyDataErrorInfoModel.cs

代码
/*
 * INotifyDataErrorInfo - 对数据实体类提供自定义验证支持,比 IDataErrorInfo 功能更强大。INotifyDataErrorInfo 支持异步验证,这就意味着其可以通过验证方法调用 Web 服务和用回调方法更新错误集合来添加服务器端验证
 *     bool HasErrors - 对象是否有验证错误信息
 *     event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged - 当对象的验证错误信息发生改变时所触发的事件
 *     IEnumerable GetErrors(string propertyName) - 获取对象的指定字段的验证错误信息
 
*/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace Silverlight40.Binding
{
    
public class INotifyDataErrorInfoModel : System.ComponentModel.INotifyDataErrorInfo
    {
        
// 验证错误的提示信息
        private const string ID_ERROR = "id 不能小于 10";
        
private const string NAME_ERROR = "name 不能包含空格";
        
private const string NAME_WARNING = "name 不能小于 5 个字符";

        
// 用于保存验证错误信息。key 保存所验证的字段名称;value 保存对应的字段的验证错误信息列表
        private Dictionary<String, List<String>> errors = new Dictionary<string, List<string>>();

        
private int _id;
        [Display(Name 
= "标识")]
        
public int Id
        {
            
get { return _id; }
            
set
            {
                
if (value > 1000)
                    
throw new Exception("太大了");

                
if (IsIdValid(value) && _id != value)
                    _id 
= value;
            }
        }

        
private string _name;
        [Display(Name 
= "名称")]
        
public string Name
        {
            
get { return _name; }
            
set
            {
                
if (IsNameValid(value) && _name != value)
                    _name 
= value;
            }
        }

        
public bool IsIdValid(int value)
        {
            
bool isValid = true;

            
if (value < 10)
            {
                AddError(
"Id", ID_ERROR);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Id", ID_ERROR);
            }

            
return isValid;
        }

        
public bool IsNameValid(string value)
        {
            
bool isValid = true;

            
if (value.Contains(" "))
            {
                AddError(
"Name", NAME_ERROR);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Name", NAME_ERROR);
            }

            
if (value.Length < 5)
            {
                AddError(
"Name", NAME_WARNING);
                isValid 
= false;
            }
            
else
            {
                RemoveError(
"Name", NAME_WARNING);
            }

            
return isValid;
        }

        
public void AddError(string propertyName, string error)
        {
            
if (!errors.ContainsKey(propertyName))
                errors[propertyName] 
= new List<string>();

            
if (!errors[propertyName].Contains(error))
            {
                errors[propertyName].Add(error);
                RaiseErrorsChanged(propertyName);

            }
        }

        
public void RemoveError(string propertyName, string error)
        {
            
if (errors.ContainsKey(propertyName) && errors[propertyName].Contains(error))
            {
                errors[propertyName].Remove(error);

                
if (errors[propertyName].Count == 0)
                    errors.Remove(propertyName);

                RaiseErrorsChanged(propertyName);
            }
        }



        
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        
public void RaiseErrorsChanged(string propertyName)
        {
            
if (ErrorsChanged != null)
                ErrorsChanged(
thisnew DataErrorsChangedEventArgs(propertyName));
        }

        
public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            
if (errors.ContainsKey(propertyName))
                
return errors[propertyName];
            
else
                
return null;
        }

        
public bool HasErrors
        {
            
get { return errors.Count > 0; }
        }
    }
}


INotifyDataErrorInfo.xaml

代码
<navigation:Page x:Class="Silverlight40.Binding.INotifyDataErrorInfo" 
           xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
           xmlns:navigation
="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:sdk
="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
           Title
="INotifyDataErrorInfo Page">
    
<Grid x:Name="LayoutRoot">
        
<StackPanel HorizontalAlignment="Left">

            
<sdk:ValidationSummary Margin="3">
                
<sdk:ValidationSummary.Header>
                    错误列表:
                
</sdk:ValidationSummary.Header>
            
</sdk:ValidationSummary>

            
<!--
                ValidatesOnExceptions - 指定绑定引擎是否报告验证过程中的异常信息
                NotifyOnValidationError - 当出现验证错误时是否触发 BindingValidationError 事件
            
-->
            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Target="{Binding ElementName=txtId}" />
                
<TextBox Name="txtId" Text="{Binding Id, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" KeyDown="txtId_KeyDown"/>
                
<sdk:DescriptionViewer Description="id 不能小于 10"/>
            
</StackPanel>

            
<StackPanel Orientation="Horizontal">
                
<sdk:Label Target="{Binding ElementName=txtName}"/>
                
<TextBox Name="txtName" Text="{Binding Name, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" KeyDown="txtName_KeyDown"/>
                
<sdk:DescriptionViewer Description="name 不能包含空格且 name 不能小于 5 个字符"/>
            
</StackPanel>

            
<Button Name="btnSubmit" Content="是否有验证错误" Click="btnSubmit_Click" />

        
</StackPanel>
    
</Grid>
</navigation:Page>


INotifyDataErrorInfo.xaml.cs

代码
/*
 * 通过绑定实现了 INotifyDataErrorInfo 接口的实体类来实现自定义数据验证功能
 
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;

namespace Silverlight40.Binding
{
    
public partial class INotifyDataErrorInfo : Page
    {
        INotifyDataErrorInfoModel _model;

        
public INotifyDataErrorInfo()
        {
            InitializeComponent();
        }

        
protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            _model 
= new INotifyDataErrorInfoModel() { Id = 100, Name = "webabcd" };
            LayoutRoot.DataContext 
= _model;

            
// BindingValidationError - 当有数据验证错误时所触发的事件。绑定时需要设置 NotifyOnValidationError=True
            txtId.BindingValidationError += (x, y) => { MessageBox.Show(y.Error.ErrorContent.ToString()); };
            txtName.BindingValidationError 
+= (x, y) => { MessageBox.Show(y.Error.ErrorContent.ToString()); };
        }

        
private void txtId_KeyDown(object sender, KeyEventArgs e)
        {
            
// 注:
            
// BindingValidationError 事件只有在控件失去焦点才会被触发
            
// 如果需要在控件没失去焦点的情况下产生验证效果,那么可以通过调用 BindingExpression.UpdateSource() 方法来实现

            
// FrameworkElement.GetBindingExpression(DependencyProperty dp) - 获取 FrameworkElement 的指定属性上的绑定信息
            
// BindingExpression.UpdateSource() - 将当前绑定信息立即发送到绑定方式为 TwoWay 的属性上
            if (e.Key == System.Windows.Input.Key.Enter)
                txtId.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }

        
private void txtName_KeyDown(object sender, KeyEventArgs e)
        {
            
if (e.Key == System.Windows.Input.Key.Enter)
                txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }

        
private void btnSubmit_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(
"是否有验证错误:" + _model.HasErrors.ToString());
        }
    }
}



注:
Silverlight 2.0 下的绑定及数据验证:http://www.cnblogs.com/webabcd/archive/2008/11/20/1337190.html
Silverlight 3.0 下的绑定:http://www.cnblogs.com/webabcd/archive/2009/09/03/1559240.html
Silverlight 3.0 下的数据验证(依赖 DataAnnotations):http://www.cnblogs.com/webabcd/archive/2009/08/10/1542663.html


OK
[源码下载]