在做表单的,需要对User提交数据做验证,wpf与silverlight 都提供自带的验证机制,但是只是验证,并不能在提交时提供详细的信息,此时可使用 依赖属性将错误信息整合进自定义错误集合中,即可在提交时获取相应错误信息方便后续业务处理。
Silverlifgt版本:
public class ValidationScope { public FrameworkElement ScopeElement { get; private set; } private readonly ObservableCollection<ValidationError> _errors = new ObservableCollection<ValidationError>(); public ObservableCollection<ValidationError> Errors { get { return _errors; } } public bool IsValid() { return _errors.Count == 0; } public static string GetValidateBoundProperty(DependencyObject obj) { return (string)obj.GetValue(ValidateBoundPropertyProperty); } public static void SetValidateBoundProperty(DependencyObject obj, string value) { obj.SetValue(ValidateBoundPropertyProperty, value); } public static readonly DependencyProperty ValidateBoundPropertyProperty = DependencyProperty.RegisterAttached("ValidateBoundProperty", typeof(string), typeof(ValidationScope), new PropertyMetadata(null)); public static ValidationScope GetValidationScope(DependencyObject obj) { return (ValidationScope)obj.GetValue(ValidationScopeProperty); } public static void SetValidationScope(DependencyObject obj, ValidationScope value) { obj.SetValue(ValidationScopeProperty, value); } public static readonly DependencyProperty ValidationScopeProperty = DependencyProperty.RegisterAttached("ValidationScope", typeof(ValidationScope), typeof(ValidationScope), new PropertyMetadata(null, ValidationScopeChanged)); private static void ValidationScopeChanged(DependencyObject source, DependencyPropertyChangedEventArgs args) { ValidationScope oldScope = args.OldValue as ValidationScope; if (oldScope != null) { oldScope.ScopeElement.BindingValidationError -= oldScope.ScopeElement_BindingValidationError; oldScope.ScopeElement = null; } FrameworkElement scopeElement = source as FrameworkElement; if (scopeElement == null) { throw new ArgumentException(string.Format( "'{0}' is not a valid type.ValidationScope attached property can only be specified on types inheriting from FrameworkElement.", source)); } ValidationScope newScope = (ValidationScope)args.NewValue; newScope.ScopeElement = scopeElement; newScope.ScopeElement.BindingValidationError += newScope.ScopeElement_BindingValidationError; } private void ScopeElement_BindingValidationError(object sender, ValidationErrorEventArgs e) { if (e.Action == ValidationErrorEventAction.Removed) { Errors.Remove(e.Error); } else if (e.Action == ValidationErrorEventAction.Added) { Errors.Add(e.Error); } } public void ValidateScope() { ForEachElement(ScopeElement, delegate(DependencyObject obj) { string propertyName = GetValidateBoundProperty(obj); if (!string.IsNullOrEmpty(propertyName)) { FrameworkElement element = (FrameworkElement)obj; var field = element.GetType().GetFields(BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) .Where(p => p.FieldType == typeof(DependencyProperty) && p.Name == (propertyName + "Property")) .FirstOrDefault(); if (field == null) { throw new ArgumentException(string.Format( "Dependency property '{0}' could not be found on type '{1}'; ValidationScope.ValidateBoundProperty", propertyName, element.GetType())); } var be = element.GetBindingExpression((DependencyProperty)field.GetValue(null)); be.UpdateSource(); } }); } private static void ForEachElement(DependencyObject root, Action<DependencyObject> action) { int childCount = VisualTreeHelper.GetChildrenCount(root); for (int i = 0; i < childCount; i++) { var obj = VisualTreeHelper.GetChild(root, i); action(obj); ForEachElement(obj, action); } } }
WPF版:
public class ValidationScope { public FrameworkElement ScopeElement { get; private set; } private readonly ObservableCollection<ValidationError> _errors = new ObservableCollection<ValidationError>(); public ObservableCollection<ValidationError> Errors { get { return _errors; } } public bool IsValid() { return _errors.Count == 0; } public static string GetValidateBoundProperty(DependencyObject obj) { return (string)obj.GetValue(ValidateBoundPropertyProperty); } public static void SetValidateBoundProperty(DependencyObject obj, string value) { obj.SetValue(ValidateBoundPropertyProperty, value); } public static readonly DependencyProperty ValidateBoundPropertyProperty = DependencyProperty.RegisterAttached("ValidateBoundProperty", typeof(string), typeof(ValidationScope), new PropertyMetadata(null)); public static ValidationScope GetValidationScope(DependencyObject obj) { return (ValidationScope)obj.GetValue(ValidationScopeProperty); } public static void SetValidationScope(DependencyObject obj, ValidationScope value) { obj.SetValue(ValidationScopeProperty, value); } public static readonly DependencyProperty ValidationScopeProperty = DependencyProperty.RegisterAttached("ValidationScope", typeof(ValidationScope), typeof(ValidationScope), new PropertyMetadata(null, ValidationScopeChanged)); private static void ValidationScopeChanged(DependencyObject source, DependencyPropertyChangedEventArgs args) { ValidationScope oldScope = args.OldValue as ValidationScope; if (oldScope != null) { oldScope.ScopeElement.RemoveHandler(System.Windows.Controls.Validation.ErrorEvent, new RoutedEventHandler(oldScope.ScopeElement_BindingValidationError)); oldScope.ScopeElement = null; } FrameworkElement scopeElement = source as FrameworkElement; if (scopeElement == null) { throw new ArgumentException(string.Format( "'{0}' is not a valid type.ValidationScope attached property can only be specified on types inheriting from FrameworkElement.", source)); } ValidationScope newScope = (ValidationScope)args.NewValue; newScope.ScopeElement = scopeElement; newScope.ScopeElement.AddHandler(System.Windows.Controls.Validation.ErrorEvent, new RoutedEventHandler(newScope.ScopeElement_BindingValidationError), true); } public void ScopeElement_BindingValidationError(object sender, RoutedEventArgs e) { System.Windows.Controls.ValidationErrorEventArgs args = e as System.Windows.Controls.ValidationErrorEventArgs; if (args.Error.RuleInError is System.Windows.Controls.ValidationRule) { BindingExpression bindingExpression = args.Error.BindingInError as System.Windows.Data.BindingExpression; string propertyName = bindingExpression.ParentBinding.Path.Path; DependencyObject OriginalSource = args.OriginalSource as DependencyObject; string errorMessage = ""; ReadOnlyObservableCollection<System.Windows.Controls.ValidationError> errors = System.Windows.Controls.Validation.GetErrors(OriginalSource); if (errors.Count > 0) { StringBuilder builder = new StringBuilder(); builder.Append(propertyName).Append(":"); System.Windows.Controls.ValidationError error = errors[errors.Count - 1]; { if (error.Exception == null || error.Exception.InnerException == null) builder.Append(error.ErrorContent.ToString()); else builder.Append(error.Exception.InnerException.Message); } errorMessage = builder.ToString(); } StringBuilder errorID = new StringBuilder(); errorID.Append(args.Error.RuleInError.ToString()); if (args.Action == ValidationErrorEventAction.Added) { Errors.Add(args.Error); } else if (args.Action == ValidationErrorEventAction.Removed) { Errors.Remove(args.Error); } } } public void ValidateScope() { ForEachElement(ScopeElement, delegate(DependencyObject obj) { string propertyName = GetValidateBoundProperty(obj); if (!string.IsNullOrEmpty(propertyName)) { FrameworkElement element = (FrameworkElement)obj; var field = element.GetType().GetFields(BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) .Where(p => p.FieldType == typeof(DependencyProperty) && p.Name == (propertyName + "Property")) .FirstOrDefault(); if (field == null) { throw new ArgumentException(string.Format( "Dependency property '{0}' could not be found on type '{1}'; ValidationScope.ValidateBoundProperty", propertyName, element.GetType())); } var be = element.GetBindingExpression((DependencyProperty)field.GetValue(null)); be.UpdateSource(); } }); } private static void ForEachElement(DependencyObject root, Action<DependencyObject> action) { int childCount = VisualTreeHelper.GetChildrenCount(root); for (int i = 0; i < childCount; i++) { var obj = VisualTreeHelper.GetChild(root, i); action(obj); ForEachElement(obj, action); } } }