silverlight wpf Command提交时输入验证

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;
            }
        }
原文地址:https://www.cnblogs.com/akiing/p/4030332.html