{"id":2867,"date":"2022-10-10T21:26:40","date_gmt":"2022-10-10T21:26:40","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=2867"},"modified":"2022-10-10T21:26:40","modified_gmt":"2022-10-10T21:26:40","slug":"wpf-validation-methods","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/wpf-validation-methods\/","title":{"rendered":"WPF Validation methods"},"content":{"rendered":"<p>How do we handle validation in WPF ? <\/p>\n<p><strong>Before we begin&#8230;<\/strong><\/p>\n<p>Let&#8217;s start by looking at the view model we&#8217;re going to work on and write our validation code for &#8211; this is a very simple model with a single property &#8220;Number&#8221; which could ofcourse represent anything you like.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class NumberViewModel : INotifyPropertyChanged\r\n{\r\n   \/\/ standard implementation of INotifyPropertyChanged removed \r\n   private int number;\r\n\r\n   public int Number\r\n   {\r\n      get { return number; }\r\n      set\r\n      {\r\n         if (number != value)\r\n         {\r\n            number = value;\r\n            OnPropertyChanged(&quot;Number&quot;);\r\n         }\r\n      }\r\n   }\r\n}\r\n<\/pre>\n<p>You can assume that OnPropertyChanged fires the INotifyPropertyChanged.PropertyChanged event, but I&#8217;ve removed the implementation for brevity.<\/p>\n<p>We&#8217;ll be equally simple with our UI, which has the following XAML<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;TextBox Text=&quot;{Binding Number}&quot; \/&gt;\r\n<\/pre>\n<p><strong>Good old IDataErrorInfo<\/strong><\/p>\n<p>So if you&#8217;ve used IDataErrorInfo in WinForms, this will be very familiar to you. We can simple implement the IDataErrorInfo interface in our NumberViewModel and add something like the following<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nstring IDataErrorInfo.Error\r\n{\r\n   get { return null; }\r\n}\r\n\r\nstring IDataErrorInfo.this&#x5B;string columnName]\r\n{\r\n   get\r\n   {\r\n      if (columnName == &quot;Number&quot;)\r\n      {\r\n         if (number &lt; 0)\r\n            return &quot;Number must be greater or equal to 0&quot;;\r\n      }\r\n      return null;\r\n   }\r\n}\r\n<\/pre>\n<p>To get the WPF binding to actually interact with the IDataErrorInfo we need to change the XAML to look like this<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;TextBox Text=&quot;{Binding Number, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}&quot; \/&gt;\r\n<\/pre>\n<p>In the above our IDataErrorInfo.this indexer will be called each time the property changes, at which time we can handle the validation either within the view model or ofcourse via some other validation rule class.<\/p>\n<p><strong>ValidationRule validation<\/strong><\/p>\n<p>An alternative to the IDataErrorInfo route for validation are ValidationRules. A ValidateRule implementation of the previous validator is listed below<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class PostiveValidationRule : ValidationRule\r\n{\r\n   public override ValidationResult Validate(object value, CultureInfo cultureInfo)\r\n   {\r\n      int result;\r\n      if (value != null &amp;&amp; Int32.TryParse(value.ToString(), out result))\r\n      {\r\n         if (result &lt; 0)\r\n            return new ValidationResult(\r\n               false, &quot;Number must be greater or equal to 0&quot;);\r\n      }\r\n      return ValidationResult.ValidResult;\r\n   }\r\n}\r\n<\/pre>\n<p>To use the above rule in XAML we write the following<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;TextBox&gt;\r\n   &lt;TextBox.Text&gt;\r\n      &lt;Binding Path=&quot;Number&quot; \r\n            ValidatesOnDataErrors=&quot;True&quot; \r\n            UpdateSourceTrigger=&quot;PropertyChanged&quot;&gt;\r\n         &lt;Binding.ValidationRules&gt;\r\n            &lt;validators:PostiveValidationRule \/&gt;\r\n         &lt;\/Binding.ValidationRules&gt;\r\n      &lt;\/Binding&gt;\r\n   &lt;\/TextBox.Text&gt;\r\n&lt;\/TextBox&gt;\r\n<\/pre>\n<p>The ValidationRules (as the pluralism suggests) can have multiple rules listed.<\/p>\n<p><strong>INotifyDataErrorInfo<\/strong><\/p>\n<p>.NET 4.5 included the INotifyDataErrorInfo. This allows us to validate in a more asynchronous way in that we can raise the ErrorsChanged event when we have errors and report them so that the binding engine can then call the GetErrors method to get the list of errors. <\/p>\n<p><strong>BindingGroup<\/strong><\/p>\n<p>The previously highlighted validation methods tend to be aimed more at a specific view model, but what if our view is made up of multiple view models and we want to validate across them all. Then we can look to use the BindingGroup.<\/p>\n<p>The <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.data.bindinggroup%28v=vs.110%29.aspx\" title=\"BindingGroup Class\" target=\"_blank\" rel=\"noopener\">BindingGroup creates a relationship between multiple bindings, which can be validated and updated together<\/a>.<\/p>\n<p>To put it another way, the BindingGroup allows us to validate a group of bindings at the same time. Our sample only has a view model but if we had multiple view models it could validate all the items that make up a BindingGroup. Let&#8217;s look at how we could create a BindingGroup.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;StackPanel&gt;\r\n   &lt;StackPanel.BindingGroup&gt;\r\n     &lt;BindingGroup Name=&quot;ValidDataGroup&quot;&gt;\r\n        &lt;BindingGroup.ValidationRules&gt;\r\n           &lt;validators:ValidDataValidationRule \/&gt;\r\n        &lt;\/BindingGroup.ValidationRules&gt;\r\n     &lt;\/BindingGroup&gt;\r\n   &lt;\/StackPanel.BindingGroup&gt;\r\n   &lt;TextBox Text=&quot;{Binding Text, ValidatesOnDataErrors=True, BindingGroupName=ValidDataGroup}&quot; \/&gt;\r\n<\/pre>\n<p>We give the BindingGroup a name and then we can assign this name to the various bindings &#8211; in this example we don&#8217;t actually need to use the group name on the textbox as it will be used within the validation, but hopefully you can see how the syntax would look.<\/p>\n<p>Now let&#8217;s take a look at the ValidDataValidationRule code<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class ValidDataValidationRule : ValidationRule\r\n{\r\n   public override ValidationResult Validate(object value, CultureInfo cultureInfo)\r\n   {\r\n      var bindingGroup = (BindingGroup)value;\r\n      if (bindingGroup != null)\r\n      {\r\n         var numberViewModel = bindingGroup.Items&#x5B;0] as NumberViewModel;\r\n         if (numberViewModel != null)\r\n         {\r\n            if (numberViewModel.Number &lt; 0)\r\n            {\r\n               return new ValidationResult(\r\n                  false, \r\n                  &quot;Number must be greater or equal to 0&quot;);\r\n            }\r\n         } \r\n      }\r\n      return ValidationResult.ValidResult;\r\n   }\t\t\r\n}\r\n<\/pre>\n<p>The Items collection will contain the various groups that have been assigned the BindingGroup name and then we can validate across all the data contexts.<\/p>\n<p>Unfortunately, this doesn&#8217;t just happen magically. Instead, we need to invoke it. If we give the name <em>DataElement<\/em> to the StackPanel and for the sake of simplicity we add a button with a Click handler, we can then call the BindingGroup&#8217;s CommitEdit method to force validation, i.e.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nDataElement.BindingGroup.CommitEdit();\r\n<\/pre>\n<p><em>Note: The default style is to draw a read line around the control which fails validation, if you&#8217;ve placed the StackPanel as the top level Window you might need to add a margin to see the red border.<\/em><\/p>\n<p><strong>ValidationStep<\/strong><\/p>\n<p>On both the BindingGroup and ValidationRule we can set the ValidationStep property. This allows us to tell the binding mechanism at what stage it should invoke the validation rule.<\/p>\n<p>This can be set to one of four values<\/p>\n<p><em>CommittedValue:<\/em> Runs the ValidationRule after the value has been committed to the source.<br \/>\n<em>ConvertedProposedValue:<\/em> Runs the Validation rule after a value is converetd.<br \/>\n<em>RawProposedValue:<\/em> Runs the validation before any conversion occurs.<br \/>\n<em>UpdatedValue:<\/em> Runs the ValidationRule after the source is updated.<\/p>\n<p><em>Note: The above definitions were taken from <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.controls.validationstep(v=vs.110).aspx\" title=\"ValidationStep Enumeration\" target=\"_blank\" rel=\"noopener\">ValidationStep Enumeration<\/a><\/em><\/p>\n<p><strong>Data annotations<\/strong><\/p>\n<p>Finally, let&#8217;s take a look at data annotations or more specifically validation attributes that are part of the <em>System.ComplonentMode.DataAnnotations<\/em> namespace.<\/p>\n<p>With the data annotations we can apply attributes to a class or its members which denote the validation rules to be used. For example<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class ValidationModel\r\n{\r\n   &#x5B;Required(ErrorMessage = &quot;First name is a required field&quot;)]\r\n   public string FirstName { get; set; }\r\n}\r\n<\/pre>\n<p><em>In this example we&#8217;ve removed the get\/set actual implementation, for brevity.<\/em><\/p>\n<p>Now this code requires use to write code to validation the property, for example in our setter we might have code like this<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nvar validationContext = new ValidationContext(this, null, null);\r\nvalidationContext.MemberName = nameof(FirstName);\r\nValidator.ValidateProperty(value, validationContext);\r\n<\/pre>\n<p><strong>Enhancing the user interface<\/strong><\/p>\n<p>So far, I&#8217;ve not done anything to make the user experience any better regarding validation, i.e. you will only see a red border around our text box, but this doesn&#8217;t really help the user identify what&#8217;s gone wrong, so now let&#8217;s look at how we might change our UI to better reflect the validation failures.<\/p>\n<p>Ofcourse, we&#8217;ve been busy creating error messages, but so far not shown them in the UI, so we could use something like the following<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;TextBox Text=&quot;{Binding Number, \r\n      UpdateSourceTrigger=PropertyChanged, \r\n      ValidatesOnDataErrors=True}&quot;&gt;\r\n   &lt;Validation.ErrorTemplate&gt;\r\n      &lt;ControlTemplate&gt;\r\n         &lt;StackPanel&gt;\r\n            &lt;AdornedElementPlaceholder \/&gt;\r\n            &lt;TextBlock Text=&quot;{Binding &#x5B;0].ErrorContent}&quot; Foreground=&quot;Red&quot;\/&gt;\r\n         &lt;\/StackPanel&gt;\r\n      &lt;\/ControlTemplate&gt;\r\n   &lt;\/Validation.ErrorTemplate&gt;\r\n&lt;\/TextBox&gt;\r\n<\/pre>\n<p>This will display the first error message just beneath the TextBox. <\/p>\n<p>Another alternative is to style the text box with a trigger against the Validation.HasError property<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;Style TargetType=&quot;{x:Type TextBox}&quot;&gt;\r\n   &lt;Style.Triggers&gt;\r\n      &lt;Trigger Property=&quot;Validation.HasError&quot; Value=&quot;True&quot;&gt;\r\n         &lt;Setter Property=&quot;Background&quot; Value=&quot;Red&quot; \/&gt;\r\n          &lt;Setter Property=&quot;ToolTip&quot;\r\n             Value=&quot;{Binding RelativeSource={x:Static RelativeSource.Self},\r\n             Path=(Validation.Errors)&#x5B;0].ErrorContent}&quot;\/&gt;\r\n      &lt;\/Trigger&gt;\r\n   &lt;\/Style.Triggers&gt;\r\n&lt;\/Style&gt;\r\n<\/pre>\n<p><strong>References<\/strong><\/p>\n<p><a href=\"http:\/\/blog.magnusmontin.net\/2013\/08\/26\/data-validation-in-wpf\/\" title=\"Data validation in WPF\" target=\"_blank\" rel=\"noopener\">Data validation in WPF<\/a><br \/>\n<a href=\"http:\/\/www.scottlogic.com\/blog\/2008\/11\/28\/using-bindinggroups-for-greater-control-over-input-validation.html\" title=\"Using BindingGroups For Greater Control Oover Input Validation\" target=\"_blank\" rel=\"noopener\">Using BindingGroups For Greater Control Over Input Validation<\/a><br \/>\n<a href=\"http:\/\/www.scottlogic.com\/blog\/2009\/01\/26\/bindinggroups-for-total-view-validation.html\" title=\"BindingGroups For Total View Validation\" target=\"_blank\" rel=\"noopener\">BindingGroups For Total View Validation<\/a><br \/>\n<a href=\"http:\/\/blogs.msdn.com\/b\/vinsibal\/archive\/2008\/08\/11\/wpf-3-5-sp1-feature-bindinggroups-with-item-level-validation.aspx\" title=\"WPF 3.5 SP1 Feature: BindingGroups with Item-level Validation\" target=\"_blank\" rel=\"noopener\">WPF 3.5 SP1 Feature: BindingGroups with Item-level Validation<\/a><br \/>\n<a href=\"http:\/\/www.codeproject.com\/Articles\/15239\/Validation-in-Windows-Presentation-Foundation\" title=\"Validation in Windows Presentation Foundation\" target=\"_blank\" rel=\"noopener\">Validation in Windows Presentation Foundation<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How do we handle validation in WPF ? Before we begin&#8230; Let&#8217;s start by looking at the view model we&#8217;re going to work on and write our validation code for &#8211; this is a very simple model with a single property &#8220;Number&#8221; which could ofcourse represent anything you like. public class NumberViewModel : INotifyPropertyChanged { [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[13],"tags":[],"class_list":["post-2867","post","type-post","status-publish","format-standard","hentry","category-wpf"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/2867","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=2867"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/2867\/revisions"}],"predecessor-version":[{"id":9558,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/2867\/revisions\/9558"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=2867"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=2867"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=2867"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}