title: WPF BindingGroup Validation
body: <p>I am using a StackPanel to host several TextBoxes that have validation rules attached.
  I also have a StackPanel.BindingGroup validation, see code below:</p>
  
  <p>I have a BindingGroup validation rule called: ValidateAll from which I would like to display the error message in a TextBlock on my StatusBar.
  I only want to display the ValidateAll message as the TextBox validation messages are displayed below the TextBoxes.</p>
  
  <p>I would like to setup a style for my TextBlock where I can display only the validation error message from my BindingGroup, (the ValidateAll rule).</p>
  
  <p>I know I can do this in code by handling the ItemError event, where I can get the rule associated with an error message, through the ValidationError.RuleInError property, (see below). </p>
  
  <p>I would like to be able to accomplish this in xaml, possibly by setting up a Style/Trigger/Setter combination to my StatusBar TextBlock.
  Any help would be much appreciated.</p>
  
  <p>Code:</p>
  
  <pre><code>using System;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  using System.Diagnostics;
  using System.ComponentModel;
  using System.Runtime.CompilerServices;
  using System.Globalization;
  
  namespace WpfGroupValidationDemo2
  {
      /// &lt;summary&gt;
      /// Interaction logic for MainWindow.xaml
      /// &lt;/summary&gt;
      public partial class MainWindow : Window
      {
          public MainWindow()
          {
              InitializeComponent();
              this.DataContext = new ViewModel();
          }
  
          private void PreviewTextBoxKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
          {
              this.TextBoxStack.BindingGroup.UpdateSources();
          }
  
          // This event occurs when a ValidationRule in the BindingGroup or in a Binding fails.
          private void ItemError(object sender, ValidationErrorEventArgs e)
          {
  
              if ((e.Action == ValidationErrorEventAction.Added) &amp;&amp;
                  (e.Error.RuleInError.ToString() == "WpfGroupValidationDemo2.ValidateAll"))
              {
                  StatusTextBlock.Text = e.Error.ErrorContent.ToString();
              }    
              else
                  StatusTextBlock.Text = String.Empty;
          }
      }
  
      public class ViewModel : INotifyPropertyChanged
      {
  
          public event PropertyChangedEventHandler PropertyChanged;   
  
          public ViewModel()
          {  
              this.name = "Allan";
              this.age = 30;
          }
  
          #region Properties
  
          private string name;
          public string Name
          {
              get { return this.name; }
              set
              {
                  if (value != name)
                  {
                      this.name = value;
                      this.OnPropertyChanged(nameof(Name));
                  }
              }
          }
          private int age;
          public int Age
          {
              get { return this.age; }
              set
              {
                  if (value != this.age)
                  {
                      this.age = value;
                      this.OnPropertyChanged(nameof(Age));
                  }
              }
          }
  
    #endregion Properties
  
          private void OnPropertyChanged([CallerMemberName] String propertyName = "")
          {
              PropertyChangedEventHandler handler = PropertyChanged;
              if (handler != null)
              {
                  handler(this, new PropertyChangedEventArgs(propertyName));
              }
          }
      }
  
    #region Validation Rules
      public class ValidateAgeRule : ValidationRule
      {
          public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
          {
              if (!int.TryParse(value.ToString(), out int i))
                  return new ValidationResult(false, "Please enter a valid integer value.");
  
              if (i &lt; 30 || i &gt; 70)
                  return new ValidationResult(false, "Age must be between 30 and 70");
  
              return new ValidationResult(true, null);
  
          }
      }
  
      public class ValidateNameRule : ValidationRule
      {
          public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
          {
              string name = (string)value;
              if (name != "Allan" &amp;&amp; name != "Jim")
                  return new ValidationResult(false, "Please enter the names: Allan or Jim");
  
              return new ValidationResult(true, null);
          }
      }
  
  
      public class ValidateAll : ValidationRule
      {
  
          public override ValidationResult Validate(object value, CultureInfo cultureInfo)
          {
              if (value == null)
                  return ValidationResult.ValidResult;
  
              BindingGroup bg = value as BindingGroup;
  
              ViewModel viewModel = bg.Items[0] as ViewModel;
  
              object ageValue;
              object nameValue;
  
              // Get the proposed values for age and name 
              bool ageResult = bg.TryGetValue(viewModel, "Age", out ageValue);
              bool nameResult = bg.TryGetValue(viewModel, "Name", out nameValue);
  
              if (!ageResult || !nameResult)
                  return new ValidationResult(false, "Properties not found");
  
              int age = (int)ageValue;
              string name = (string)nameValue;
  
              if ((age == 30 ) &amp;&amp; (name == "Jim"))
                  return new ValidationResult(false, "Jim cannot be Thirty!");
  
              return ValidationResult.ValidResult;
          }
      }
  
    #endregion Validation Rules
  }
  </code></pre>
  
  <p>XAML:</p>
  
  <pre><code>Window x:Class="WpfGroupValidationDemo2.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:local="clr-namespace:WpfGroupValidationDemo2"
      mc:Ignorable="d"
      Title="MainWindow" Height="450" Width="800"&gt;
  
      &lt;Window.Resources&gt;
          &lt;ControlTemplate x:Key="validationTemplate" &gt;
              &lt;StackPanel&gt;
                  &lt;!--Placeholder for the TextBox itself--&gt;
                  &lt;AdornedElementPlaceholder/&gt;
                  &lt;TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red" Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}"/&gt;
              &lt;/StackPanel&gt;
          &lt;/ControlTemplate&gt;
  
          &lt;!-- Add a red border on validation error to a textbox control --&gt;
          &lt;Style x:Key="TextBoxBorderStyle" TargetType="TextBox"&gt;
              &lt;Setter Property="Template"&gt;
                  &lt;Setter.Value&gt;
                      &lt;ControlTemplate TargetType="{x:Type TextBox}"&gt;
                          &lt;Border x:Name="bg" BorderBrush="#FFABADB3" BorderThickness="1"&gt;
                              &lt;ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/&gt;
                          &lt;/Border&gt;
                          &lt;ControlTemplate.Triggers&gt;
                              &lt;Trigger Property="Validation.HasError" Value="True" &gt;
                                  &lt;Trigger.Setters&gt;
                                      &lt;Setter Property="BorderBrush" TargetName="bg"  Value="Red"/&gt;
                                      &lt;Setter Property="BorderThickness" TargetName="bg" Value="1"/&gt;
                                      &lt;Setter Property="SnapsToDevicePixels" TargetName="bg" Value="True"/&gt;
                                  &lt;/Trigger.Setters&gt;
                              &lt;/Trigger&gt;
                          &lt;/ControlTemplate.Triggers&gt;
                      &lt;/ControlTemplate&gt;
                  &lt;/Setter.Value&gt;
              &lt;/Setter&gt;
          &lt;/Style&gt;
  
      &lt;/Window.Resources&gt;
  
      &lt;Grid&gt;
          &lt;StackPanel HorizontalAlignment="Left" Height="204" Margin="168,125,0,0" VerticalAlignment="Top" Width="409" RenderTransformOrigin="0.5,0.5" Orientation="Horizontal"&gt;
              &lt;StackPanel Width="184" HorizontalAlignment="Right"&gt;
                  &lt;Label Content="Name:" HorizontalAlignment="Right" Margin="0,3"/&gt;
                  &lt;Label Content="Age:" HorizontalAlignment="Right"/&gt;
              &lt;/StackPanel&gt;
          &lt;StackPanel  Name="TextBoxStack" Width="200" Height="202" Validation.ErrorTemplate="{x:Null}" Validation.Error="ItemError"&gt;
              &lt;StackPanel.BindingGroup&gt;
                  &lt;BindingGroup Name="ValidateAllFields" NotifyOnValidationError="True"&gt;
                      &lt;BindingGroup.ValidationRules&gt;
                          &lt;local:ValidateAll ValidationStep="ConvertedProposedValue"/&gt;
                      &lt;/BindingGroup.ValidationRules&gt;
                  &lt;/BindingGroup&gt;
              &lt;/StackPanel.BindingGroup&gt;
              &lt;TextBox x:Name="NameTextBox" Style="{StaticResource TextBoxBorderStyle}" TextWrapping="Wrap" Height="26" VerticalContentAlignment="Center" 
                           Margin="0,3,130,3" Validation.ErrorTemplate="{StaticResource validationTemplate}" PreviewKeyUp="PreviewTextBoxKeyUp"&gt;
                  &lt;TextBox.Text&gt;
                      &lt;Binding Path="Name" UpdateSourceTrigger="PropertyChanged"&gt;
                          &lt;Binding.ValidationRules&gt;
                              &lt;local:ValidateNameRule ValidationStep="RawProposedValue" ValidatesOnTargetUpdated="True"/&gt;
                          &lt;/Binding.ValidationRules&gt;
                      &lt;/Binding&gt;
                  &lt;/TextBox.Text&gt;
              &lt;/TextBox&gt;
              &lt;TextBox x:Name="AgeTextBox" Style="{StaticResource TextBoxBorderStyle}" Height="26" TextWrapping="Wrap" VerticalContentAlignment="Center" 
                           Margin="0,0,130,3" Validation.ErrorTemplate="{StaticResource validationTemplate}" PreviewKeyUp="PreviewTextBoxKeyUp"&gt;
                  &lt;TextBox.Text&gt;
                      &lt;Binding Path="Age" UpdateSourceTrigger="PropertyChanged"&gt;
                          &lt;Binding.ValidationRules&gt;
                              &lt;local:ValidateAgeRule ValidationStep="RawProposedValue" ValidatesOnTargetUpdated="True"/&gt;
                          &lt;/Binding.ValidationRules&gt;
                      &lt;/Binding&gt;
                  &lt;/TextBox.Text&gt;
              &lt;/TextBox&gt;
          &lt;/StackPanel&gt;
      &lt;/StackPanel&gt;
      &lt;Label Content="BindingGroup Demo" HorizontalAlignment="Left" Margin="204,78,0,0" VerticalAlignment="Top" Width="305"/&gt;
          &lt;Label Content="Only Visible when All the textboxes pass validation!" HorizontalAlignment="Left" Margin="417,332,0,0" VerticalAlignment="Top" Width="286" &gt;
              &lt;Label.Style&gt;
                  &lt;Style TargetType="{x:Type Label}"&gt;
                      &lt;Setter Property="Visibility" Value="Hidden" /&gt;
                      &lt;Style.Triggers&gt;
                          &lt;!-- Require the controls to be valid in order to be visible --&gt;
                          &lt;MultiDataTrigger&gt;
                              &lt;MultiDataTrigger.Conditions&gt;
                                  &lt;Condition Binding="{Binding ElementName=NameTextBox, Path=(Validation.HasError)}" Value="false" /&gt;
                                  &lt;Condition Binding="{Binding ElementName=AgeTextBox, Path=(Validation.HasError)}" Value="false" /&gt;
                                  &lt;Condition Binding="{Binding ElementName=TextBoxStack, Path=(Validation.HasError)}" Value="false" /&gt;
                          &lt;/MultiDataTrigger.Conditions&gt;
                              &lt;Setter Property="Visibility" Value="Visible" /&gt;
                          &lt;/MultiDataTrigger&gt;
                      &lt;/Style.Triggers&gt;
                  &lt;/Style&gt;
              &lt;/Label.Style&gt;
          &lt;/Label&gt;
          &lt;StatusBar Margin="4,0,0,1" VerticalAlignment="Bottom" VerticalContentAlignment="Bottom" Padding="0,3" &gt;
              &lt;StatusBarItem&gt;
                  &lt;TextBlock Name="StatusTextBlock" Foreground="Red" /&gt;
              &lt;/StatusBarItem&gt;
          &lt;/StatusBar&gt;
      &lt;/Grid&gt;
  </code></pre>
  
  <p></p>