Before directly getting into implementation, let me explain the problem scenario or use case so that you can understand the motivation behind this implementation.
Recently, I was working on a simple WPF application which will take few inputs and create or update records in database, a quite simple requirement. On my UI, I wanted to have some validation. So, I used WPF ValidationRule. Now with ValidationRule the challenge that I faced is, these ValidationRule does not execute unless there is an update that happens to binding source.
To elaborate more on the problem scenario, say we have 5 inputs for which we have created ValidationRule and among them, 3 are mandatory fields. Now, this ValidationRule gets evaluated only when an update to source binding happens. For user input validation, it is fine because users will provide some input and that will trigger an update to source binding. But for other three mandatory inputs, if user does not even input anything and proceeds to submit the button, the ValidationRule for mandatory inputs will not trigger as there is no action to update source binding. Those ValidationRules for mandatory field remain unexecuted and do not cause any validation error even user has not input anything for mandatory field.
Ways to approach it
Solution to this problem is to find some way to trigger update to source binding on submit button click. One way could be explicit UpdateSourceTrigger in UI code behind on button click. This approach will work but this way we will be violating MVVM pattern and also it will be tedious if we have a number of such mandatory input in several UI screens. Other way to handle using IDataErrorInfo, approach of using IDataErrorInfo interface will work fine with MVVM. But one thing that I personally did not like about this approach is that it causes all validation rules to execute on the very first time when view gets loaded and shows all validation error on load itself without giving any opportunity to user to input data. Basically, I wanted to have a feature that will evaluate all mandatory fields ValidationRule only when user tries to submit all inputs instead of evaluating all mandatory fields on very first time on view load. I am not sure whether there is any tweak to have this feature implemented using IDataErrorInfo/INotifyDataErrorInfo interface. Hence I did an implementation to have this feature in MVVM way and I will be explaining that in the subsequent sections of this article. If you want to have similar feature in MVVM way and you are not finding any other way to do it, I hope this will help you.
Implementation Details
Creation of ValidationRule and Validation ErrorTemplate is out of scope. It is assumed that audience have understating of MVVM, how to create and apply Validation rule for a control.
I have used Prism’s InteractionRequest for popup notification but that is not required you can simply use MessageBox to show any such notification.
I have created a BaseView control which inherits from a Window control, please note you can also use UserControl as a Parent type of your BaseView. In my reference Demo implementation I have named this as FormValidationBaseWindowControl. These BaseView control has two dependency property and one Attached Property.
- ValidationRequestCount
This is a dependency property and it needs to incremented every time a Mandatory input validation check is requested.
- IsValidated
This is also a dependency property which indicates whether all mandatory fields are provided or not.
- MandatoryInputBinding
This is an attached property and needs to be attached with mandatory input control. Value to be set for this property is a Dependency property for which you have a ValidationRule. Say you have a mandatory TextBox input and for Text property you have a binding with ValidationRule. In this case you have to attach MandatoryInputBinding attached property in TextBox and value to be set is TextProperty dependency property. Simply the value for the attached property to be set is the Dependency property for which you have a binding expression with ValidationRule. Belo show a sample code snippets showing usage of MandatoryInputBinding attached property.
- <TextBox Width="200" local:FormValidationBaseWindowControl.MandatoryInputBinding="TextProperty">
- <TextBox.Text>
- <Binding Path="Name" Mode="TwoWay" UpdateSourceTrigger="LostFocus">
- <Binding.ValidationRules>
- <local:EmptyStringValidation /> </Binding.ValidationRules>
- </Binding>
- </TextBox.Text>
- </TextBox>
Now, the UI screen where you want to have this feature of mandatory input Validation before inputs are submitted, you can inherit your view from this BaseView and provide appropriate binding in ViewModel for ValidationRequestCount, IsValidated Dependency property and attach MandatoryInputBinding attached property to those input where you have ValidationRule.
In your ViewModel, on button submit, you need to increment ValidationRequestCount which will trigger ValidationRequestCount DependencyProperty change callback. This property change callback will traverse the logical tree of the entire view and check for the controls that have set MandatoryInputBinding attached property. It then finds the binding expression for the controls that have the set MandatoryInputBinding attached property and explicitly triggers UpdateToSourceTrigger which causes the underlying ValidationRule to execute.
Based on the ValidationRule evaluation, it then sets IsValidated Dependency Property to either true or false. True if all ValidationRules eare valuated successfully and false if at least one Validation rule failed. Based on this flag in View model you can proceed for further processing or else you can return and show Validation error in the UI with a custom/default Validation ErrorTemplate. In Demo Application I have used default error template. Source code for Demo reference implementation is uploaded.
It is a very basic implementation just to share the idea and this can be enhanced further if there is any sophisticated requirement.
Thanks for reading till the end and I hope this helps you.