WPF 3.5 SP1 Feature: BindingGroups with Item-level Validation


Motivation


Before 3.5 SP1, the binding validation system worked with only one binding at a time.  What made this difficult in some scenarios was when validation was required on a set of bound objects.  Some typical examples that come to mind are a form of data items that are submitted all at once or a ListView or DataGrid row that is committed all at once.  You can put a validation rule on each bound object and validate their values individually but there was no readily available way in the binding validation system to validate multiple objects together.  Now in 3.5 SP1 a solution has been created for this, Binding Groups.


 


What is it?


A BindingGroup encapsulates and has access to a set of related bindings.  These related bindings are bindings that share the same data context as the BindingGroup and/or bindings that have explicitly declared membership to the BindingGroup.  With this set of bindings, the BindingGroup can now provide services such as validation of all the encapsulated bindings together and transactional editing. 


 


How do I define a BindingGroup?


We will use an example to illustrate how to define a BindingGroup.  Let’s say this is the data context that I will use (I just went canoeing recently which influence my choice of data):      



public class BoatRentalCustomer


{


  public string FirstName { get; set; }


  public string LastName { get; set; }


  public DateTime? DateOfBirth { get; set; }


  public int BoatCheckOutID { get; set; }


  public DateTime? StartDate { get; set; }


  public DateTime? EndDate { get; set; }


}  


 


With this data I want to use it in a form to be filled in by the customer.  Here is the xaml (I only put the relevant information for this example):



<Grid>


  <StackPanel>


    <TextBox Text=”{Binding Path=FirstName}” />


    <TextBox Text=”{Binding Path=LastName}” />


    <TextBox Text=”{Binding Path=DateOfBirth}” />


    <TextBox Text=”{Binding Path=BoatCheckOutID}” />


    <TextBox Text=”{Binding Path=StartDate}” />


    <TextBox Text=”{Binding Path=EndDate}” />


    <Button Click=”submit_Click”>Submit</Button>


  </StackPanel>


</Grid>  


 


As I said in the Motivation section, we can add validation to each binding and validate them individually, but there wasn’t a built in mechanism to validate them as a group.  To do that, we can add a BindingGroup to the Grid:



<Grid>


  <Grid.BindingGroup>


    <BindingGroup>


      <BindingGroup.ValidationRules>


       


      </BindingGroup.ValidationRules>


    </BindingGroup>


  </Grid.BindingGroup>


  <StackPanel>


    <TextBox Text=”{Binding Path=FirstName}” />


    <TextBox Text=”{Binding Path=LastName}” />


    <TextBox Text=”{Binding Path=DateOfBirth}” />


    <TextBox Text=”{Binding Path=BoatCheckOutID}” />


    <TextBox Text=”{Binding Path=StartDate}” />


    <TextBox Text=”{Binding Path=EndDate}” />


    <Button Click=”submit_Click”>Submit</Button>


  </StackPanel>


</Grid>  


 


Assuming that I’ve set the Grid.DataContext to be an instance of BoatRentalCustomer, the BindingGroup I set on the Grid will have access to all the bindings I set within the Grid.  This example shows how the BindingGroup gets bindings from the same DataContext but it is also possible to get bindings through explicit membership declaration like this slightly modified example below:



<Grid>


  <Grid.BindingGroup>


    <BindingGroup Name=”FormBindingGroup”>


      <BindingGroup.ValidationRules>


       


      </BindingGroup.ValidationRules>


    </BindingGroup>


  </Grid.BindingGroup>


  <StackPanel>


    <Slider Name=”sliderFontSize” Minimum=”1″ Maximum=”100″ Value=”10″ />


    <TextBox Text=”{Binding Path=FirstName}”


             FontSize=”{Binding ElementName=sliderFontSize,


                               Path=Value,


                               BindingGroupName=FormBindingGroup}” />


    <TextBox Text=”{Binding Path=LastName}” />


    <TextBox Text=”{Binding Path=DateOfBirth}” />


    <TextBox Text=”{Binding Path=BoatCheckOutID}” />


    <TextBox Text=”{Binding Path=StartDate}” />


    <TextBox Text=”{Binding Path=EndDate}” />


    <Button Click=”submit_Click”>Submit</Button>


  </StackPanel>


</Grid>  


 


 


I named the BindingGroup, “FormBindingGroup”, and added a binding to the FontSize property on one of the TextBox elements.  Notice that in the binding I explicitly declare BindingGroupName to be set to “FormBindingGroup”.  By doing this, the BindingGroup will have access to the bindings under the same DataContext as well as the bindings associated with it by the same BindingGroupName.  The ability to declare explicit membership to a BindingGroup are for scenarios where bindings don’t use DataContext (explicit Source or ElementName like in the example above) and/or bindings that use a different DataContext that want to participate in the same group validation.


From the previous example you saw that Grid had a property named BindingGroup.  You may be wondering where BindingGroup is actually defined.  BindingGroup has been added to FrameworkElement and FrameworkContentElement so it can be associated with a set of bindings on a per element basis.      



public class Framework[Content]Element


{


  public static readonly DependencyProperty BindingGroupProperty;


  public BindingGroup BindingGroup { get; set; }


}


 


It has also been added to ItemsControl so it can be associated with a set of bindings per each generated container.  The pattern is similar to ItemTemplate.



public class ItemsControl


{   


  public static readonly DependencyProperty ItemBindingGroupProperty;


  public BindingGroup ItemBindingGroup { get; set; }


}  


 


And as you saw the use of BindingGroupName, that property has been defined in BindingBase. 



public class BindingBase


{


    public string BindingGroupName { get; set; }


}  


 


How do I use it? (Part 1)


So now I know where to define a BindingGroup and how bindings get associated with it, but how do I use it?  Let’s first look at the BindingGroup class and then dig into the common APIs and common scenarios.  Here is the BindingGroup class:



public class BindingGroup : DependencyObject


{


  public Collection<BindingExpressionBase> BindingExpressions { get; }


  public bool CanRestoreValues { get; }


  public IList Items { get; }


  public string Name { get; set; }


  public bool NotifyOnValidationError { get; set; }


  public Collection<ValidationRule> ValidationRules { get; }


 


  public void BeginEdit();


  public void CancelEdit();


  public bool CommitEdit();


  public object GetValue(object item, string propertyName);


  public bool TryGetValue(object item, string propertyName, out object value);


  public bool UpdateSources();


  public bool ValidateWithoutUpdate();


}


 


You have access to the associated bindings through the BindingExpressions property and can set group validation rules through the ValidationRules property.  You may have notice all these transactional methods in this class such as BeginEdit, CancelEdit, etc.  Well, as part of group validation you have the ability to control whether the new data to that item is committed as a whole and/or reverted back to its previous state.  The two major areas of BindingGroup that I want to get into are its validation rules and how its methods are used to update the binding sources.  First, let’s discuss validation. 


The ValidationRule class has been updated with a couple APIs to work with BindingGroup.  Here are the new APIs:



public abstract class ValidationRule


{


  public bool ValidatesOnTargetUpdated { get; set; }


  public ValidationStep ValidationStep { get; set; }


}


 



public enum ValidationStep


{


  RawProposedValue = 0,


  ConvertedProposedValue = 1,


  UpdatedValue = 2,


  CommittedValue = 3,


}


 


The ValidationStep property is used to let ValidationRule know when to apply the validation.  RawProposedValue means the rule is applied to the unconverted value.  This is the default step which also was the current behavior for ValidationRule prior to this feature.  ConvertedProposedValue means the rule is applied to the converted value, UpdatedValue means the rule is applied after writing to the source, and CommittedValue means the rule is applied after committing changes.  ValidatesOnTargetUpdated is used to trigger validation when the source is updating the target. 


So now we have an idea of how validation rule may be used but how does this relate to the transactional methods of BindingGroup?  Note that to use a BindingGroup for transactional editing, you should define IEditableObject on your data item. 


BeginEdit, CancelEdit, and CommitEdit work similar to IEditableCollectionView’s versions where they will call IEditableObject.BeginEdit, IEditableObject.CancelEdit, and IEditableObject.EndEdit respectively on the data item.  In addition to CommitEdit, you also have UpdateSources and ValidateWithoutUpdate.  These three methods (CommitEdit, UpdateSources, and ValidateWithoutUpdate) are the main methods that you will call to validate and update your source.  That brings up a point on how bindings update in a BindingGroup.  UpdateSourceTrigger for bindings that belong to the BindingGroup are set to Explicit by default.  That means you will need to use the BindingGroup APIs to update the bindings (CommitEdit or UpdateSources).  Validation and updating methods work like this:


·         ValidateWithoutUpdate: runs all validation rules marked as RawProposedValue or ConvertedProposedValue.


·         UpdateSources: does the same as ValidateWithoutUpdate, and then if no errors were found, it writes all the values to the data item and runs the validation rules marked as UpdateValue.


·         CommitEdit: does the same as UpdateSources, and then runs the rules marked as CommittedValue.


 


How do I use it? (Part 2)


Ok, we made it this far.  So I know where to define my BindingGroup, how to setup when I want my validation rules ran and how to update my bindings.  Let’s look at what kinds of things I can do to actually validate the item inside the ValidationRule.Validate method.


The value input parameter to Validate is the actual BindingGroup.  With the BindingGroup you can query for the data on the item.  The important BindingGroup APIs for the actual validation include Items, GetValue, and TryGetValue.  The Items collection contains the unique data items used by the bindings in the group.  GetValue looks for the binding that uses the given item and propertyName parameter values and returns the value appropriate to the current validation step.  So for example, if the validation step is RawProposedValue or ConvertedProposedValue, the data source has not been updated yet when the Validate method is called, but by querying for the value using GetValue you can get what the value will be (as long as it passes validation).  Here is an example Validate method:



public override ValidationResult Validate(object value, CultureInfo cultureInfo)


{


  BindingGroup bindingGroup = (BindingGroup)value;


  BoatRentalCustomer customer = (BoatRentalCustomer)bindingGroup.Items[0];


 


  object startDateObj = bindingGroup.GetValue(customer, “StartDate”);


  DateTime? startDate = DateTime.Parse((string)startDateObj);


 


  object endDateObj = bindingGroup.GetValue(customer, “EndDate”);


  DateTime? endDate = DateTime.Parse((string)endDateObj);


 


  // check start and end date together


  if (startDate.Value > endDate.Value)


  {


      return new ValidationResult(false, string.Format(


        “StartDate: {0}, cannot be greater than EndDate: {1}”,


        startDate,


        endDate));


  }


  else


  {
      return
new ValidationResult(true, null);
  }


}


 


You’ll notice that I am assuming the data item is Items[0].  If the BindingGroup has bindings from more than one data source then you will have to handle that as the Items collection will be greater than one.  I’ve also been pretty optimistic about getting the proposed values as well as parsing the DateTime.  This example is for demonstration purposes only but in a production level application you will probably want to use TryGetValue and TryParse.  So inside the Validate method I have access to the entire item and in the example I validate the start and end date together.


 


Where we are…


Hopefully I still have your attention after all that.  I know it’s a lot to go through.  Just to summarize a bit, a BindingGroup enabled you to validate data at the item-level as opposed to the property-level.  This is possible because the BindingGroup has access to all the bindings associated with the data item (assuming you set the BindingGroup that way).  BindingGroup is available on all FrameworkElements, FrameworkContentElements, and ItemsControls.  You set the validation rules on the actual BindingGroup and in the ValidationRule.Validate method you are given the BindingGroup where you can query for all the properties on the item being validated.  I’ve attached a small sample to demonstrate some of the concepts discussed here. 


So what’s left?  Well, there are two more important topics that should be discussed.  One is the new additions to validation feedback and the other is how BindingGroup works in conjunction with IEditableCollectionView.  I will save both these topics for another post as there is enough here to digest.  This does cover about 95% of the new APIs relating to BindingGroup so there isn’t too much more to cover.  = )


However, BindingGroup and IEditableCollectionView can be a bit confusing on when and how to use them so I do want to talk more on that.  Read more on BindingGroup and IEditableCollectionView here.  And you can read on BindingGroup and ValidationFeedback here.

BindingGroupSample.zip

Comments (39)

  1. Josh Smith says:

    Thanks for the explanation.  I like what I see with these new features, but..my gawd, WPF’s support for input validation is getting quite complicated.  Unless I’m missing something, it seems that most, if not all, of this extra complexity could be avoided by simply implementing IDataErrorInfo on your domain classes and letting the inter-property validation occur in the domain layer.

    Josh

  2. I definitely agree that it can be quite complicated and validation design can also be a difficult subject.  There are a lot of different best practices on where you should place your validation logic and as WPF’s framework is designed, it is trying to be as flexible as possible.  Of course with flexibility comes complexity.  

    For the IDataErrorInfo scenario you can use the item-level validation like so:

    public class DataErrorItemValidationRule :

           ValidationRule

    {

     public override ValidationResult Validate(

         object value,

         CultureInfo cultureInfo)

     {

       BindingGroup bindingGroup = (BindingGroup)value;

       IDataErrorInfo idei = bindingGroup.Items[0] as IDataErrorInfo;

       string error = (idei != null) ? idei.Error : null;

       return (string.IsNullOrEmpty(error)) ?

             ValidationResult.ValidResult :

             new ValidationResult(false, error);

     }

    }

    This simplifies the Validate method quite a bit and leaves the validation logic in the domain layer but even so, it doesn’t make the BindingGroup feature that much easier to use.  

    Complexity is a big topic in WPF as I know you are obviously aware, even from the WPF Disciple disucssions.  It is something we are trying to fix in future releases.  It is actually unfortunate for the BindingGroups feature since it wasn’t ready in the SP1 beta timeframe for feedback.  But if you do have any suggestions we are definitely listening (especially for this BindingGroup feature).

    By the way, I did read your article in MSDN Magazine on validation and binding.  Really good one man!  

  3. Josh Smith says:

    Thanks Vincent.  I meant that you could avoid using BindingGroups altogether by implementing IDataErrorInfo and setting the ValidatesOnDataError property of Binding to true.  It seems to me that the impetus behind BindingGroups is to allow you to implement validation that takes multiple related properties into account (ex. Min cannot be greater than Max).  Using UI-centric validation, via WPF’s validation rule system, it’s complicated.  But, using the IDataErrorInfo, it’s easy because you already have access to all of your domain objects and their properties in the place where the validation logic exists.  It obviates the need for BindingGroups.

    My point is, all of this extra functionality is great and I’m sure some people will find it very useful.  For me, though, it seems like the product of a heavily UI-centric mode of thinking.  It’s generally considered a bad practice to put domain rules into classes with an affinity to a specific UI platform.

    My 2 cents…

    (Thanks for the kudos on my MSDN article!)

    Josh

  4. Hi Vincent – thanks for this great post – I’ve been curious about the BindingGroup ever since it was mentioned in the SP1 feature list. It’s good to finally see the details!

    I’d partially agree with Josh in that having a purely UI based validation system does run against the grain – I would have been much happier to see the original WPF Validation classes divorced from WPF and put in their own System.Data.Validation namespace.

    Having said that – at work we’re currently writing a WPF application that uses a combination of Castle’s Validation attributes together with custom validation logic baked into the data model. I personally believe that IDataErrorInfo itself is flawed – the very fact that it returns a single string to report validation messages limits its use (what if there are multiple errors, what if we have different priorities and severities, what about summary vs extended erorr info/help?). Our current approach is to make use of IDataErrorInfo and ValidatesOnDataErrors to trigger validation and then have our own custom code to report rich error information.

    Hopefully I’ll be able to post on our progress with this and I think we may be able to make use of BindingGroup in regard to entity based validation and cancel/commit features. I look forward to your upcoming article on BindingGroup with IEditableCollectionView!

  5. swythan says:

    Is there any way to make the BindingGroup automatically commit/validate when any of the binding sources changes?

    I want to use IDataErrorInfo (or preferably the VAB) in a situation where the validation may cross-reference property values. I don’t want the user to have to click a button to see if their values are valid, I want to commit the values from the UI to the binding targets immediately and show validation feedback (e.g. including disabling a WPF command if the new state is invalid).

  6. swythan,

    As you can see from the BindingGroup APIs, the BindingGroup’s intent is to be used in a transactional way.  When a full row is committed, validation occurs.  If it fails you can roll back or allow the user to cancel.  What you want to do is run the item level validation when any bindings have been updated so you may not be able to take advantage of the transactional behavior.  With that said, you could listen to binding target changes and call bindingGroup.ValidateWithoutUpdate there.  Bindings would have already been updated so there is no need to call bindingGroup.GetValue in the Validate method anymore.  You can just get the values directly from bindingGroup.Items[x].

  7. Josh,

    While I really cannot argue against your thoughts on validation design (as I generally agree with you), I do want to point out what BindingGroups can enable.  In particular, the convenience in validation for a DataGrid or DataGrid like control.  

    With the WPF CTP DataGrid you are able to open a row for edit (F2) and commit that data (ENTER).  While you can add property level validation to all your bindings and set the ValidatesOnDataError property, when a validation rule fails on commit, you as the app author will have to handle all the rest of the values in the row that have already been committed.  Since the row has already been committed, IEditableObject.EndEdit will have already been called which may clear your cache of row data to revert back to.  With BindingGroups, the app author just needs to provide a item-level validation rule and when that validation rule fails on commit, the app author does not have to write more custom code to get the behavior to either revert back or keep the current state without committing. (Note: row validation is actually not implemented yet in the CTP even if it says it is)

    So with a mechanism like BindingGroups, it makes it easier for the app author to use the DataGrid control and row validation right out of the box.  As far as where the validation logic goes, the app author can still utilize IDataErrorInfo and keep it in the domain layer.

  8. Josh Smith says:

    Vincent,

    You wrote:

    "…when a validation rule fails on commit, you as the app author will have to handle all the rest of the values in the row that have already been committed.  Since the row has already been committed, IEditableObject.EndEdit will have already been called which may clear your cache of row data to revert back to."

    It strikes me as odd that EndEdit() is invoked for the row before its new values are validated.  In my mind, an editing session is not over until the user’s new input is validated and pushed to the data source object.  Ending an editing session before validating the new values seems like a bad idea, because of exactly the problem you pointed out in your explanation of why BindingGroup is necessary.

    Is there some compelling reason to call EndEdit() on the data source before it has a chance to validate the new values?  Or, did I misunderstand your point altogether?

    Thanks,

    Josh

  9. I guess what I’m describing is a little unfair b/c it was written with BindingGroups in mind.  The behavior that I’m describing is the current behavior of the WPF DataGrid (which is without row validation) and how it can be solved with BindingGroups (which is what the PDC version will have).  If there was no such thing as BindingGroups then this issue of EndEdit being called before validation would not be the functionality.  We would create a mechanism so validation would occur before the commit (in this case, BindinGroups).  It just so happened that row validation didn’t make the CTP so you see a half implemented version of the transaction system in the DataGrid.  

    So I guess what I meant to point out was the motivation and solution we choose to do validation in a transaction scenario for the DataGrid and how BindingGroups fit in nicely.  I hope I’m making sense.  Sorry for the confusion.

  10. Josh Smith says:

    Thanks again, Vincent.  I guess the question of whether the control should ask the object for validation errors before or after it calls EndEdit() should be answered by seeing what the WinForms DataGridView does.  Considering that many WinForms apps will be ported to WPF, the WPF grid should not introduce breaking changes to things as core as object editing and reporting validation errors.  I don’t know what that standard behavior is off the top of my head.  🙂

    Thanks,

    Josh

  11. Mike Strobel says:

    The problem I see with this BindingGroup design is that any validation that is performed by attaching ValidationRules to an invividual Binding within a BindingGroup needs to be duplicated in the BindingGroup itself.  Otherwise, the BindingGroup will successfully validate even when those rules fail.  Consider this scenario:

    <StackPanel>

     <StackPanel.BindingGroup>

       <BindingGroup />

     </StackPanel.BindingGroups>

     <Label Content="Name:" />

     <TextBox>

       <TextBox.Text>

         <Binding Path="Description"

                  Mode="TwoWay">

           <Binding.ValidationRules>

             <local:NonEmptyValidationRule ValidatesOnTargetUpdated="True"

                                           ValidationStep="ConvertedProposedValue"

                                           ErrorMessage="You must enter a Description." />

           </Binding.ValidationRules>

         </Binding>

       </TextBox.Text>

     </TextBox>

    </StackPanel>

    The NonEmptyValidationRule will not be checked when validating/committing the BindingGroup.  Moreover, if you move all of the validation rules into the BindingGroup, you lose the ability to show ErrorTemplates for the individual data-bound controls–instead, the ErrorTemplate is shown for the element on which the BindingGroup is attached.

    I have created a special ValidationRule that is intended to solve this problem.  Simply attach it to the BindingGroup.ValidationRules collection, and it will verify the ValidationRules on the individual Bindings:

    public class BindingGroupMemberValidationRule : ValidationRule

    {

       public override ValidationResult Validate(object value, CultureInfo cultureInfo)

       {

           var bindingGroup = value as BindingGroup;

           if (bindingGroup == null)

               return ValidationResult.ValidResult;

           foreach (var bindingExpression in bindingGroup.BindingExpressions)

           {

               if (bindingExpression.HasError)

                   return new ValidationResult(false, bindingExpression.ValidationError.ErrorContent.ToString());

           }

           return ValidationResult.ValidResult;

       }

    }

    Regards,

    Mike

  12. "The NonEmptyValidationRule will not be checked when validating/committing the BindingGroup."

    That is actually incorrect.  In a BindingGroup.CommitEdit() it will first check for validation rules on specific bindings then run the validation rules set on the BindingGroup.  The problem that you are probably seeing is that even if the validation rule on the specific binding fails, it will still run the validation rules on the BindingGroup instead of stopping as soon as the specific validation rule fails.  But all in all, it will run all validaiton rules.

    "Moreover, if you move all of the validation rules into the BindingGroup, you lose the ability to show ErrorTemplates for the individual data-bound controls–instead, the ErrorTemplate is shown for the element on which the BindingGroup is attached."

    This is also incorrect.  If you move all the validation rules into the BindingGroup you can still show the ErrorTemplate on any specific control within the group through ValidationAdornerSiteFor.  I was planning on discuss the usage of that API in a follow-up post.  Also, if you have validaiton rules set in a BindingGroup and individual bindings, it will show the ErrorTemplate for both the BindingGroup and the individual element bindings.  

    You can still write the code in the Validate method you provide to stop validation before you start verifying combinations of properties.

  13. Mike Strobel says:

    Thanks for your reply, Vincent.

    "That is actually incorrect.  In a BindingGroup.CommitEdit() it will first check for validation rules on specific bindings then run the validation rules set on the BindingGroup.  The problem that you are probably seeing is that even if the validation rule on the specific binding fails, it will still run the validation rules on the BindingGroup instead of stopping as soon as the specific validation rule fails.  But all in all, it will run all validaiton rules."

    Allow me to correct my previous statement.  The rules set on individual bindings *are* being invoked by the BindingGroup (or so it seems), but the failure of those rules does not prevent CommitEdit nor ValidateWithoutUpdate from succeeding.  That is, both of those methods are returning true, even when validation rules set on the individual bindings are failing.  That is why I found it necessary to create the custom validation rule above, which would propagate the failure of any individual binding rule up to the binding group.  Is this behavior by design?  It hardly seems useful for a BindingGroup to validate the rules on individual bindings if the results are effectively ignored.

    "This is also incorrect.  If you move all the validation rules into the BindingGroup you can still show the ErrorTemplate on any specific control within the group through ValidationAdornerSiteFor.  I was planning on discuss the usage of that API in a follow-up post.  Also, if you have validaiton rules set in a BindingGroup and individual bindings, it will show the ErrorTemplate for both the BindingGroup and the individual element bindings."

    Apologies.  My second claim was based on the assumption that my first claim was correct.  I meant to say that if CommitEdit was succeeding even when the rules on the individual bindings were failing, then one option would be to remove those validation rules and replace them with a sort of "composite" rule attached to the binding group (as in your sample).  In that case, the error templates of the data-bound controls would no longer function, as the validation rules had been (re-)moved.  Obvious alternatives would be to set rules on both the individual bindings as well as the binding group (unnecessarily redundant), or to use a custom rule like the one I provided above.

  14. Mike Strobel says:

    A correction to my second post: the validation rules on the individual bindings are being evaluated on a call to CommitEdit, but not ValidateWithoutUpdate.  All of my rules have ValidationStep set to ‘ConvertedProposedValue’.

  15. K. M. says:

    I am combining the use of IDataErrorInfo with a BindingGroup to achieve both property level and item level validation. I have posted some code <a href="http://softnotes.wordpress.com/2008/08/20/validation-in-wpf-with-net-35-sp1/">here</a&gt;.

    Basically I have put all the validation related functionality along with handling commands for Edit, Accept, Cancel in a static class that provides attached properties. I also call EditItem, CommitEdit and CancelEdit on the Items collection of a ItemsControl if the element with the BindingGroup is generated by an ItemsControl. I would be grateful if you could take a look. I am particularly interested in knowing whether my approach will work well with IEditableCollectionView.

  16. Overall, what you are doing is an optimal alternative to the current design.  But let me clarify on a couple things just to be clear on what is going on in the BindingGroup code.

    On your first comment:

    "The rules set on individual bindings *are* being invoked by the BindingGroup (or so it seems), but the failure of those rules does not prevent CommitEdit nor ValidateWithoutUpdate from succeeding.  That is, both of those methods are returning true, even when validation rules set on the individual bindings are failing."

    This is partially correct except for the CommitEdit and ValidateWithoutUpdate part.  Here is basically how it works:

    1. when you can bindingGroup.CommitEdit()

    2. CommitEdit() calls UpdateAndValidate(ValidationStep.CommittedValue)

    3.  and the algorithm basically looks like this:

    bool UpdateAndValidate(ValidationStep validationStep)

        {

               bool result = true;

               for (_validationStep = ValidationStep.RawProposedValue;

                       _validationStep <= validationStep;

                       ++ _validationStep)

               {

                   switch (_validationStep)

                   {

                       case ValidationStep.RawProposedValue:

                           _getValueTable.ResetValues();

                           break;

                       case ValidationStep.ConvertedProposedValue:

                           ObtainConvertedProposedValues();

                           break;

                       case ValidationStep.UpdatedValue:

                           UpdateValues();

                           break;

                       case ValidationStep.CommittedValue:

                           CommitValues();

                           break;

                   }

                   if (!CheckValidationRules())

                   {

                       result = false;

                       break;

                   }

               }

               _validationStep = (ValidationStep)(-1);

               _getValueTable.ResetValues();

               return result;

           }

    What’s important to note about this method is the call to CheckValidationRules().  That is where the individual validation rules then the binding group validation rules get called.  So during that CommitEdit, it will first run all your validation rules and assuming the individual validation rule failed, nothing is committed and the return value is false. So CommitEdit and ValidateWithoutUpdate should be returning false if any type of validation rule fails.

    BUT, like you are finding out, even if the individual validation rule fails, your binding group rules are still being processed as if there was no failure.  In turn, you either have to make a duplicate check or do something like you are doing in your example.

    Now, what you are probably concerned with more and I really forgot to answer your question in the first place is if this is by design.  The answer is yes.  I understand your pain but the problem from a framework developer point of view is that we cannot know your intentions with the way you setup validation.  If we set it so that BindingGroup validation rules are only ran if all individual validation rules succeed, there are scenarios where people would not want that either.  This is something I’ve talked with the developer on and we are looking into this for future iteration.  

    Thanks a lot for all your feedback.

  17. K.M., thanks for sharing this info.  I was actually working on a BindingGroups with IEditableCollectionView post early this morning and plan to finish it by the end of the week.  I’ll also take a look at the pattern your’re using and provide some feedback.  Give me a day or two though as I have to balance this all out with the one thing we all love…work = ).

  18. Mike Strobel says:

    Thanks, Vincent.  I am actually not concerned that my BindingGroup rules are being validated when the rules on the individual bindings fail.  Your design decision here is correct in my opinion.  My problem is that that CommitEdit and ValidateWithoutUpdate are, in fact, returning ‘true’ when there is a validation failure on one of the individual bindings.  Below I have pasted some debug statements from my Immediate window, along with the evaluation results.  In this example, ‘this’ is a UserControl, and the two BindingExpressions are for bindings on text boxes within the UserControl.  The statements below were entered sequentially, and no other code ran between them.

    this.BindingGroup.BindingExpressions[0].HasError

    true

    this.BindingGroup.BindingExpressions[1].HasError

    true

    this.BindingGroup.ValidateWithoutUpdate()

    true

    this.BindingGroup.BindingExpressions[0].HasError

    true

    this.BindingGroup.BindingExpressions[1].HasError

    true

    this.BindingGroup.CommitEdit()

    true

    this.BindingGroup.BindingExpressions[0].HasError

    true

    this.BindingGroup.BindingExpressions[1].HasError

    true

    Can you shed some light on what I am seeing?  I am now quite confused ;).

  19. Mike,

    After further investigation in the code and with others, this is a known issue.  It just cropped up very differently than was originally found.  Since I don’t have all your code to look at I can’t say the most specific reason why it was failing but when you set the individual validation to have a ValidationStep.ConvertedProposedValue and bad input is given, the binding will silently swallow the failure and the BindingGroup validation will not see that.  This issue will be fixed in a future release.  

    In the meantime, if you can settle for the individual validation to have a ValidationStep.RawProposedValue then you won’t encounter this issue of seeing the validation pass at the BindingGroup level but fail at the BindingExpression level.  Hope that helps.  

  20. Mike Strobel says:

    Vincent,

    Your explanation explains the behavior I have been experiencing.  I’m relieved to know that (a) I am not going crazy, and (b) CommitEdit *should* fail when rules on individual bindings fail.  I am quite satisfied with the BindingGroup architecture now.  Thank you.

    I will use ValidationStep.RawProposedValue when possible.  As a temporary safeguard until a fix is available, I suggest invoking CommitEdit only after confirming that no validation errors have occurred:

    if (!Validation.GetHasError(this) && this.BindingGroup.CommitEdit())

    {

       // Code to run on successful commit

    }

    Cheers,

    Mike

  21. I think that is an optimal solution as a temporary safeguard.  Thanks for all your feedback.  Getting developer feedback like this definietly helps to make our product better.

  22. Recap In a previous post I introduced the BindingGroups. Well now I want to get into some of the things

  23. Elise's blog says:

    Stéphane Goudeau m’a remonté un lien ce matin : Un nouveau billet vient de sortir, qui présente une nouvelle

  24. Benjamin says:

    Hi Vincent,

    I wonder if it’s possible to add more than one BindingGroup.

    Is BindingGroup only for Validation purposes? Are there other scenarios in which BindingGroup could be usefull.

    Benjamin

    <Grid>

    <Grid.BindingGroups>

     <BindingGroup Name="Group1"/>

     <BindingGroup Name="Group2"/>

    </Grid.BindingGroups>

    <TextBox Text="{Binding LastName, BindingGroupName=Group1}"/>

    <TextBox Text="{Binding FirstName, BindingGroupName=Group1}"/>

    <TextBox Text="{Binding DateOfBirth, BindingGroupName=Group2}"/>

    <TextBox Text="{Binding Age, BindingGroupName=Group2}"/>

    </Grid>

  25. Benjamin,

    You can have multiple binding groups but there is only one BindingGroup per FrameworkElement.  So if your Window had multiple Grids then you can add a different BindingGroup per Grid.

    BindingGroup is primarily used from item-level validation but there may be other uses with it.  With BindingGroup, you have access to all the bindings within the DataContext so you can easily find a particular binding to update, check for a particular value, etc.  If you have a scenario where querying a set of bindings often, BindingGroup can help.  Like I said earlier though, its primary use is for item-level validation.  

  26. Recap In a previous set of posts I covered the introduction to the BindingGroup feature and I expanded

  27. ayordanov says:

    Hi Vincent,

    What i was unable to find out is how to check if we have data to commit. Let say i want the submit button in your example to be enabled only when there is data to submit.

    One way I have managed to do that so far is to have a property IsModified in the data class which is hooked to INotifyPropertyChanged and changes accordingly. But with BindingGroup in use the UpdateSourceTrigger is set to Explicit and the properties of the object are not changed until we call CommitEdit.

  28. ayordanov,

    If you use it with IEditableCollectionView you will know when it is in edit mode.  Of course that is different than knowing when the data is dirty.  As you know, BindingGroup has an UpdateSourceTrigger of Explicit so there really isn’t an easy to know if the data is dirty from the BindingGroup itself.  And checking the PropertyChange on the data source won’t work until you commit like you’ve said.  If you do want to check if the data is dirty and use the BindingGroup you can check the data in the actual UIElement which I believe are TextBlocks in this case.  It doesn’t really sounds like the best solution, I know.  

    Could you explain your scenario a bit more.  It may help me understand other ways for a workaround.

  29. ayordanov says:

    The scenario is pretty common. I have a form that represents some data object(which is the form’s DataContext) and it should behave differently if the data is dirty. For instance, a "*" should be visible in the title and it should warn the user if he tries to close it, just like in Visual Studio.

    I’m not using BindingGroups so far, but for the form validation i do need the list of all binding expressions in it and right now i’m doing this with some sort of a "hack".

    As you pointed out in your blog post BindingGroups are used for exactly what I need – Item-Level validation. In my case Form-Level validation. But without the ability to know when my data is dirty I can’t use them.

  30. ayordanov,

    When using BindingGroup you have access to all the bindings.  Another method of checking if the data is dirty is to use BindingGroup.TryGetValue() which will return the proposed value before the data is committed.  With that value, you can compare to your data source and update the IsModified accordingly.  

  31. John Mairs says:

    In your example StartDate and EndDate are optional; hence DateTime?.  If both of them are null CustomValidationRules returns ValidResult.

    What do you do when StartDate is not optional and cannot be null but you want to hit the cancel button to "reset" the form so that you can "save" your other changes that were made on the dialog.

    example: I know I’m taking the boat out today, I think I know when I’m bringing it back, but then change my mind to leave it open ended.

    1) enter valid info for name, start date

    2) enter invalid data for end date -1 lets say

    3) hit submit get error on end date

    4) give up on this entry,hit cancel so that I can

      make the red box go away.  

    Basically I want the cancel button to clear any textboxes and clear any validation errors

  32. John,

    It looks like the solution you are looking for is what the DataGrid will provide out of the box.  Basically as you enter data in each cell of a row, if you enter invalid data in one cell you can press ‘Esc’ and that cell will be reverted but the rest of the cells will remain.  Another ‘Esc’ will revert the rest of the cell if you want.  If you want to do that in a form like the one in this example, you will have to create a mechanism similar to the DataGrid where it keeps track of the current cell separate from the complete form.  You can download the DataGrid source here if you want to see how they are implementing it, http://www.codeplex.com/wpf.

    As far as not being optional for the DateTime on StartDate or EndDate, you can do that but obviously there is going to have to be a value there even if the user does not enter anything.  One possibility is to coerce the values through the binding system so they will always have a default value.  

  33. Hans-Christian Knöppler says:

    Hi,

    recently I tried the method suggested by Josh Smith, using IDataErrorInfo to implement the validation on the custom objects. It seems to me, however, that the scenario I wanted to implement is not supported by the WPF validation system.

    Let me explain. In your above example, when creating a new BoatRentalCustomer, as a minimum the form should tell the user which fields are required and which are optional. So, when a new instance of BoatRentalCustomer is displayed on the form, validation should take place in order to retrieve this information from the class, or from the BindingGroup, if one uses external validation rules. However, it seems as if the validation rules are only called after the user has changed something.

    Let’s assume further that there was a way to make a reservation for a future date, but that the reservation would only be valid after a down payment has been made. In this case, there should be a validation rule that says the StartDate must be in the future, and a new field that says whether the down payment has been made or not. When an existing BoatRentalCustomer is viewed, there should be a notification to the user if StartDate is not in the future, depending on whether the down payment has been made or not. In this case, too, it would be necessary to run the validation process, even though the user hasn’t changed any data.

    Meaning, changing the current instance of the bound object should trigger validation. At the moment, this isn’t the case, is it?

  34. Hans-Christian Knöppler,

    While the WPF Validation system will need a trigger of some sort to do validation, it doesn’t mean that your data in the data source has to change if you use a ValidationStep such as RawProposedValue or CovertedProposedValue.  Also, if you need to set off validation at some specific point in time (like when it is initialized) you are free to call ValidationWithoutUpdate().  

    Note: If you decide to use the IDataErrorInfo validation strategy, then the data will be updated on your data source before validation can occur.  This is by design.

  35. Hans-Christian Knöppler says:

    Hi,

    sorry for my broken English. Maybe I didn’t express myself very clearly. What I meant is the following: If the custom class supports change notification (i.e. INotifyPropertyChanged) and raises a PropertyChanged event, imho this should trigger validation, in order to give the system a chance to notify the user of any action he or she needs to take because of the change. Second, if the DataContext property itself is set to reference a different (or first) instance, imho this also should trigger validation.

    The validation system is designed only for the path from the control to its underlying property, not for the other way around; did I understand that correctly? Is this on purpose? Why?

    Of course, one could listen to the PropertyChanged and DataContextChanged events, and call validation manually. On the other hand, that wouldn’t be too straight forward either, since one would have to know whether the binding was updating the underlying property, in which case validation was already done and shouldn’t be repeated.

  36. Hans-Christian Knöppler says:

    Hi,

    some more testing turned out that what I experienced is not a matter of the validation system not validating, but of the ErrorTemplate not being shown.

    The data validation documentation led me to think that validation is only performed in response to user input. However, it runs also when the data context is set to a new instance, or when the business object raises PropertyChanged, and even when the page is displayed for the first time. The only problem is that the ErrorTemplate for a control doesn’t become visible unless the user changes the value of the control. This might be a bug…

  37. Joris Bos says:

    Hi Vincent,

    I’m having a problem defining a Binding Group. I have Business Logic defined in my class with IDataErrorInfo but it seems that these bindings are not re-evaluated when i add these textbox bindings to a Bindinggroup? So validationerrors occur, that works fine, problem is the DataErrorInfo doesn’t seem to re-evaluate..

  38. Steve buddy says:

    nice article but the how would one adjust the code so the tooltip error would only show up on the text box that has the error