다음을 통해 공유


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

  • Anonymous
    August 12, 2008
    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

  • Anonymous
    August 12, 2008
    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!  

  • Anonymous
    August 12, 2008
    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

  • Anonymous
    August 12, 2008
    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!

  • Anonymous
    August 12, 2008
    The comment has been removed

  • Anonymous
    August 13, 2008
    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].

  • Anonymous
    August 13, 2008
    The comment has been removed

  • Anonymous
    August 13, 2008
    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

  • Anonymous
    August 13, 2008
    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.

  • Anonymous
    August 13, 2008
    The comment has been removed

  • Anonymous
    August 20, 2008
    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

  • Anonymous
    August 20, 2008
    "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.

  • Anonymous
    August 20, 2008
    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.

  • Anonymous
    August 20, 2008
    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'.

  • Anonymous
    August 20, 2008
    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>. 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.

  • Anonymous
    August 20, 2008
    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.
  • Anonymous
    August 20, 2008
    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 = ).

  • Anonymous
    August 20, 2008
    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 ;).

  • Anonymous
    August 21, 2008
    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.  

  • Anonymous
    August 21, 2008
    The comment has been removed

  • Anonymous
    August 21, 2008
    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.

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

  • Anonymous
    September 01, 2008
    Stéphane Goudeau m'a remonté un lien ce matin : Un nouveau billet vient de sortir, qui présente une nouvelle

  • Anonymous
    September 03, 2008
    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>

  • Anonymous
    September 04, 2008
    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.  

  • Anonymous
    September 08, 2008
    Recap In a previous set of posts I covered the introduction to the BindingGroup feature and I expanded

  • Anonymous
    September 16, 2008
    The comment has been removed

  • Anonymous
    September 18, 2008
    The comment has been removed

  • Anonymous
    September 18, 2008
    The comment has been removed

  • Anonymous
    September 19, 2008
    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.  

  • Anonymous
    September 26, 2008
    The comment has been removed

  • Anonymous
    September 30, 2008
    The comment has been removed

  • Anonymous
    December 20, 2008
    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?

  • Anonymous
    December 21, 2008
    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.

  • Anonymous
    December 31, 2008
    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.

  • Anonymous
    December 31, 2008
    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...

  • Anonymous
    February 17, 2009
    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..

  • Anonymous
    March 01, 2009
    The comment has been removed

  • Anonymous
    March 01, 2009
    Steve buddy, Take a look at this post, http://blogs.msdn.com/vinsibal/archive/2008/09/08/wpf-bindinggroup-and-validation-feedback.aspx.