INotifyDataErrorInfo doesn't report TryValidateObject results to ValidationSummary

Topics: Prism v4 - Silverlight 4
Nov 24, 2010 at 3:22 PM

Hello everyone,

First of all - I've discovered MVVM and Prism 3 weeks ago - it's incredible how the SL application are written in this style. Thank you for this technology and this framework (SDK).

Then, I have a problem - I spent more then 2 days browsing the internet for some solutions and I couldn't find anything exact, all that I've found was only some pieces.

Now, the description (simple as possible) of my solution: I have an MVVM SL 4 application with Models, ViewModels, Viewes, Modules, etc... Everything is working great but I will give you an example for a viewmodel:

 

public class LoginChildViewModel : ViewModelBase
    {
        #region Private properties
        private IEventAggregator eventAggregator;
        private IUnityContainer unityContainer;
        private SecurityBusinessServicesClient securityServiceClient;

        private bool isLoading;

        private string username;
        private string password;
        #endregion

        #region Public properties
        /// <summary>
        /// Gets or sets the is loading.
        /// </summary>
        /// <value>The is loading.</value>
        public bool IsLoading
        {
            get { return this.isLoading; }
            set
            {
                if (this.isLoading == value)
                    return;

                this.isLoading = value;
                this.RaisePropertyChanged(() => this.IsLoading);
            }
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        /// <value>The username.</value>
        [Required(ErrorMessage = "Camp obligatoriu.")]
        [Display(Name = "Nume utilizator")]
        [StringLength(30, MinimumLength = 3, ErrorMessage = "Minim 3 caractere, maxim 50 caractere.")]
        public string Username
        {
            get 
            {
                return this.username; 
            }
            set
            {
                if (this.username == value)
                    return;

                this.ValidateProperty("Username", value);
                
                this.username = value;
                this.RaisePropertyChanged (() => this.Username);
            }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The password.</value>
        [Required(ErrorMessage = "Camp obligatoriu.")]
        [Display(Name = "Parola")]
        [StringLength(30, MinimumLength = 6, ErrorMessage = "Minim 6 caractere, maxim 50 caractere.")]
        public string Password
        {
            get 
            {
                return this.password; 
            }
            set
            {
                if (this.password == value)
                    return;

                this.ValidateProperty("Password", value); 
                
                this.password = value;
                this.RaisePropertyChanged(() => this.Password);
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="LoginChildViewModel"/> class.
        /// </summary>
        public LoginChildViewModel()
        {
			
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LoginChildViewModel"/> class.
        /// </summary>
        /// <param name="eventAggregator">The event aggregator.</param>
        /// <param name="unityContainer">The unity container.</param>
        /// <param name="securityClient">The security client.</param>
        public LoginChildViewModel(IEventAggregator eventAggregator, IUnityContainer unityContainer, SecurityBusinessServicesClient securityClient)
            : this()
        {
            this.eventAggregator = eventAggregator;
            this.unityContainer = unityContainer;
            this.securityServiceClient = securityClient;

            this.securityServiceClient.AuthenticateUserCompleted += new EventHandler<AuthenticateUserCompletedEventArgs>(securityServiceClient_AuthenticateUserCompleted);
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Authenticates the user.
        /// </summary>
        public void AuthenticateUser()
        {
            bool isViewModelValid = this.ValidateViewModel();
            if (!isViewModelValid)
                return;

            this.IsLoading = true;
            this.securityServiceClient.AuthenticateUserAsync(this.Username, this.Password);
        }
        #endregion

        #region WCF Events
        void securityServiceClient_AuthenticateUserCompleted(object sender, AuthenticateUserCompletedEventArgs e)
        {
            this.IsLoading = false;
        }
        #endregion
    }

 

Now, the standard behavior when the user enters something in one of the textboxes binded to Username or Password VM properties and then deletes the values, the validation summary from the View works and displays the errors. But, as you've seen above, I have a method ValidateViewModel which just validates the ViewModel object as an entire piece. The code follows:

/// <summary>
        /// Validates the view model.
        /// </summary>
        /// <returns></returns>
        protected bool ValidateViewModel()
        {
            ValidationContext validationContext = new ValidationContext(this, null, null);
            
            List<ValidationResult> validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(this, validationContext, validationResults);

            this.ErrorsContainer.SetErrors(validationContext.MemberName, validationResults);
            
            return (validationResults.Count == 0);
        }

This method is getting called on the ViewModel's AuthenticateUser method - and it really works from the logic point of view because it's getting the errors as it's supposed to behave BUT all the ValidationResults generated by this method are not displayed in the validation summary and I'm going crazy because I don't understand why!!!!! Please take in consideration that the implementation of INotifyDataErrorInfo is done as described in the chm doc of PRISM 4. 

Please give me some clues!

 

Thank you.

Nov 24, 2010 at 6:18 PM

Hi,

Nice to see that you started using Prism. There is not guidance on this exact topic in Prism out-of-the-box. So, I think that you can find the following link interesting, since another user achieved a similar scenario:

Hope this helps.

Fernando Antivero
http://blogs.southworks.net/fantivero

Nov 24, 2010 at 7:19 PM

Hi,

Thank you very much for your quick response. I've read the posts thar you've told me to. From what I've seen overthere that it's not similar with my issue because in my case (as I've said in my initial post), the ValidateObject really works and it collects real issues and it put them in the right collection - my issue is that those errors are not listed within the ValidationSummary.

So, to take it step by step...

a) In the VM I have a method / command which is getting called when a button from the V is clicked.

        #region Public methods
        /// <summary>
        /// Authenticates the user.
        /// </summary>
        public void AuthenticateUser()
        {
            bool isViewModelValid = this.ValidateViewModel();
            if (!isViewModelValid)
                return;

            this.IsLoading = true;
            this.securityServiceClient.AuthenticateUserAsync(this.Username, this.Password);
        }
        #endregion

b) The VM contains some properties signed with DataAnnotations attributes as follows:

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        /// <value>The username.</value>
        [Required(ErrorMessage = "Camp obligatoriu.")]
        [Display(Name = "Nume utilizator")]
        [StringLength(30, MinimumLength = 3, ErrorMessage = "Minim 3 caractere, maxim 50 caractere.")]
        public string Username
        {
            get 
            {
                this.ValidateProperty("Username", this.username);
                return this.username; 
            }
            set
            {
                if (this.username == value)
                    return;

                this.ValidateProperty("Username", value);
                
                this.username = value;
                this.RaisePropertyChanged (() => this.Username);
            }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The password.</value>
        [Required(ErrorMessage = "Camp obligatoriu.")]
        [Display(Name = "Parola")]
        [StringLength(30, MinimumLength = 6, ErrorMessage = "Minim 6 caractere, maxim 50 caractere.")]
        public string Password
        {
            get 
            {
                this.ValidateProperty("Password", this.password);
                return this.password; 
            }
            set
            {
                if (this.password == value)
                    return;

                this.ValidateProperty("Password", value); 
                
                this.password = value;
                this.RaisePropertyChanged(() => this.Password);
            }
        }

 c) If I'm writing something in the textboxes and then I'm deleting those values or I do something that is against the validation attributes (by getting into the setters of the properties) the ValidationSummary reports correctly those errors.

d) In the BaseViewModel class I have a method which validates the entire ViewModel instance as follows:

        /// <summary>
        /// Validates the view model.
        /// </summary>
        /// <returns></returns>
        protected bool ValidateViewModel()
        {
            ValidationContext validationContext = new ValidationContext(this, null, null);
            
            List<ValidationResult> validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(this, validationContext, validationResults);

            this.ErrorsContainer.SetErrors(validationContext.MemberName, validationResults);
            
            return (validationResults.Count == 0);
        }

e) The 3rd code line in my method which actually validates the object instance (TryValidateObject) does the trick all the time, even when properties are empty and the user pressed on the buttons with an emtpy form, and it puts into the validationResults list the correct errors BUT the validation errors doesn't show up into the ValidationSummary - the only difference that I found using the debugger was that the validationContext has the MemberName set as Empty which means that the validation is for the object not for a member...

So - when TryValidateObject collects the data and the errorcontainer reports them, the ValidationSummary doesn't display them... Is anyone who has any ideea - I think that is something related with the ValidationSummary.

 

Nov 26, 2010 at 8:09 PM

Hi,

There is no similar issues reported so far. So, If you continue experiencing this situation in your application, could you please share a repro sample?

Fernando Antivero
http://blogs.southworks.net/fantivero

 

 

Nov 27, 2010 at 8:20 AM

Hi,

 

I will gladly do it. Being the first time when I'm doing this, I would like to ask you if it's ok to be a zip file with a VS 2010 SL 4 Solution?

Thanks again,

Evdin

Nov 29, 2010 at 4:08 PM

Hi Evdin,

Right, that might be helpful to analyze your scenario. I think that you could share this uploading it to the cloud, for example skydrive.live.com. Then you could post the URL here, so the community will also be able to contribute.

Fernando Antivero
http://blogs.southworks.net/fantivero