Saturday, November 29, 2014

IDataErroInfo in MVVM +Presenter


(1) MVVM INPC not compativle with Validation Event so OnPropertyChanged instead
(2) Consider Grid Cell validation and Ok button CanExecute
(3) IDXDataErrorInfo is for view,VM validation required custom code
(4) IDXDataErrorInfo GetError Not implemented=> avoid clustered error on row level


      #region Validation for VM

        private bool _isValid;

        public bool IsValid
        {
            get {  return _isValid; }
            set
            {
                if (_isValid != value)  // check to reduce chking CanExcecute
                {
                    _isValid = value;
                    NotifyPropertyChanged(() => IsValid);
                }
            }
        }

        public void Validate()
        {
            int invalidChildrenCount= Children.Count(IsChildInvalid);
            bool allDataFieldValid = Validate("Pro1") == null && Validate("Pro2") ;
            IsValid= allDataFieldValid && invalidChildrenCount= == 0;
        }

        private bool IsChirldInvalid(ChirldGridVMData Chirld)
        {
            
            switch (SomeMode)
            {
                case "Mode1":
                    if (Chirld.Validdate("Prop1") != null) return true; 
                    break; 
                ...
            }

            switch (Mode2)
            {
                ...
            }

            return false; 
        }



        private string Validate(string dataFieldName)
        {

            switch (dataFieldName)
            {
                case "VMProp1":
                    if (string.IsNullOrEmpty(Description))
                    {
                        return "Please enter ..."; // Non-empty description
                    }
                    break;
              ...
            }

            return null;
        }

        public string EnsureReferenceChirldChecked()
        {
            if (Chirlds.Count(l => l.IsRefChirld.HasValue && l.IsRefChirld.Value) >= 2)
            {
                return "Only one Chirld can be reference.";
            }

            if (Chirlds.Count(l => l.IsRefChirld.HasValue && l.IsRefChirld.Value) == 0)
            {
                return "At least one Chirld must be reference.";
            }
            return null;
        }

        #endregion

        #region IDataErrorInfo for View

        public string this[string dataFieldName]
        {
            get
            {
                //CommandManager.InvalidateRequerySuggested(); does not work
                var result = Validate(dataFieldName);
                return result;
            }
        }

        public string Error
        {
            get
            {
                // return this[string.Empty or Null] does not work
                return null;
            }
        }

        #endregion

    public class LegGridVMData : ViewModelItemBase, IDXDataErrorInfo
    {
        #region Fields

        private string _p1;
        public string VMP1
        {
            get { return _p1;}
            set
            {
                _p1= value;
                NotifyPropertyChanged(() => WMP1);
            }
        }

  

        #endregion

        #region IDXDataErrorInfo for View


        public void GetError(ErrorInfo info)
        {
          // chose not to cluster error message to row level
        }

        public void GetPropertyError(string propertyName, ErrorInfo info)
        {
            info.ErrorType = ErrorType.Information;
            info.ErrorText= Validdate(propertyName);
        }

        #endregion

        #region Validation for VM

        public string Validdate(string propertyName)
        {
            switch (propertyName)
            {
                case "VMP1":
                    if (string.IsNullOrEmpty(VMP1))
                    {
                        return "Please select a VMp1";
                    }
                    break;
                ...
           

            
            }
            return null;
        }



        #endregion



        private bool CanExecuteOk()
        {
            return ViewModel.IsValid;
        }

        private void TriggerViewModelChirldsValidation()
        {
            ViewModel.Chirlds = ViewModel.Chirlds;  // Hack: assign itself to trigger Validation, w/o makeing NotifyPropertyChanged public.
        }


       void ListenToPropertyChangedEvent()
        {
            if (ViewModel == null || ViewModel.Chirlds == null) return;
            foreach (var l in ViewModel.Chirlds)
                l.PropertyChanged += OnPropertyChanged;

            ViewModel.PropertyChanged += OnPropertyChanged;
        }


        void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsValid")  // IsValid changes => reevaluate CanExecute.
            {
                var cmd = ViewModel.OkCommand as DeChirldateCommand;
                if (cmd != null) cmd.RaiseCanExecuteChanged();
            }
            else  // otherwise, just do re-evaludation of IsValid
            {
                ViewModel.Validate();
            }

            if (e.PropertyName == "IsRef")
            {
                TriggerViewModelChildrenValidation();  // Trigger one and only one Ref validation on Ref checkbox
            }

        }