silverlight 或WPF在MVVM模式中使用INotifyDataErrorInfo接口对输入进行验证时
控件lostFocus时会触发验证,但在提交动作(例如button的Command)时,不触发
验证。下面的方法提供控件输入验证统一触发。
1、添加ValidationScope类
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) { // TODO - some of this reflection could be cached to improve performance 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); } } }
2、在包含输入控件的容器控件添加依赖属性ValidationScope(ViewModel里的ValidationScope实例)
xaml:
<containers:ContainerPanel Grid.Row="1" Margin="10" Title="挂起工单列表" TitleHorizontalAlignment="Left" TitleMargin="10,10,0,10" validation:ValidationScope.ValidationScope="{Binding ValidationScope1}">
3、每个需要验证的控件添加依赖属性ValidateBound,指定要验证控件的哪个属性
<TextBox Text="{Binding ApproveContent,Mode=TwoWay}" validation:ValidationScope.ValidateBoundProperty="Text"/>
4、ViewModel中定义ValidationScope实例
private ValidationScope _validationScope1 = new ValidationScope(); public ValidationScope ValidationScope1 { get { return _validationScope1; } set { _validationScope1 = value; this.RaisePropertyChanged(() => ValidationScope1); } }
5、在要触发验证的Command中调用验证方法
public DelegateCommand ApproveCommand { get { if (_approveCommand == null) { _approveCommand = new DelegateCommand(() => { ValidationScope1.ValidateScope(); if (!ValidationScope1.IsValid()) { CommonNotification.Raise(new Notification() { Content = new UWay.NOAP.DTO.MsgInfo() { IsSuccess = true, Content = "要审批的工单需要填写审批内容!" }, Title = "提示" }); } else { ApproveBatch(); } }, () => _canApprove); } return _approveCommand; } }