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;

            }

        }

 

你可能感兴趣的:(silverlight)