Nathan Ernst
Nathan Ernst

Reputation: 4590

BindingGroup with multiple (failing) ValidationRules

I ran into a problem today at work wherein I have a BindingGroup that has multiple ValidationRules that are failing simultaneously. Problem is, I get an ArgumentException bubbling up from withing BindingGroup.ValidateWithoutUpdate when I try and determine if there are any errors (i.e. to set the CanExecute on a command to false).

I've managed to distill it to the following example (sorry, it still crosses multiple sources, but I've enclosed all relevant parts that should be copy/pasteable into a new WPF project):

Window1.xaml:

<Window 
    x:Class="BindingGroupTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:BindingGroupTest"
    xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    Title="Window1" Height="300" Width="300">

    <Window.BindingGroup>
        <BindingGroup Name="RootBindingGroup">
            <BindingGroup.ValidationRules>
                <l:TestRule1 />
                <l:TestRule2 />
            </BindingGroup.ValidationRules>
        </BindingGroup>
    </Window.BindingGroup>

    <StackPanel>
        <TextBox Text="{Binding 
            Path=Name, 
            BindingGroupName=RootBindingGroup,
            UpdateSourceTrigger=PropertyChanged,
            diag:PresentationTraceSources.TraceLevel=High}" />
        <TextBox Text="{Binding 
            Path=Age, 
            BindingGroupName=RootBindingGroup,
            UpdateSourceTrigger=PropertyChanged,
            diag:PresentationTraceSources.TraceLevel=High}" />
        <Button Content="Validate" Click="DoValidate" />
    </StackPanel>
</Window>

Window1.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace BindingGroupTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            this.DataContext = new Person()
            {
                Name="Test",
                Age=30,
            };

            InitializeComponent();

            this.BindingGroup.BeginEdit();
        }

        private void DoValidate(object sender, RoutedEventArgs e)
        {
            try
            {
                if (!this.BindingGroup.ValidateWithoutUpdate())
                    MessageBox.Show("Validation failed!");
                else
                    MessageBox.Show("Validation passed!");
            }
            catch (Exception ex)
            {
                var msg = "While validating, caught exception: " + ex.Message;
                MessageBox.Show(msg);
            }
        }
    }
}

Person.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BindingGroupTest
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

TestRule1.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace BindingGroupTest
{
    public class TestRule1 : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            var p = ((BindingGroup)value).Items[0] as Person;
            if (p == null)
                return ValidationResult.ValidResult;

            if (p.Age < 0)
                return new ValidationResult(false, "Surely, you've been born yet!");

            return ValidationResult.ValidResult;
        }
    }
}

TestRule2.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace BindingGroupTest
{
    public class TestRule2 : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            var p = ((BindingGroup)value).Items[0] as Person;
            if (p == null)
                return ValidationResult.ValidResult;

            if (string.IsNullOrEmpty(p.Name))
                return new ValidationResult(false, "What, no name?");

            return ValidationResult.ValidResult;
        }
    }
}

Basically, my problem is that if both TestRule1 and TestRule2fail, I get anArgumentExceptionbubbling up when I callthis.BindingGroup.ValidateWithoutUpdate()with message "Cannot have duplicates in this Collection", parameter name: "validationError". I dug around through the implementation ofBindingGroup, and it seems that it is using itself as the second parameter toValidationError, which is thebindingInErrorparameter, which the underlyingValidationErrorCollection` requires to be unique.

Admittedly, this example is contrived, however, it perfectly demonstrates my real-world issue that is not. (I have 2, entirely independent, ValidationRules that are operating on different attributes of the same business object, and it would make zero sense to collapse these into a single ValidationRule). Additionally, every example I can find of using BindingGroup only ever demonstrates use of a single ValidationRule, but the construct clearly supports and accepts multiple rules, albeit, apparently, so long as only a single fails at a time.

Am I doing something wrong, or does this appear to be a bug in the BindingGroup implementation, as I'm inclined to think currently.

For what it's worth, I'm using VS2008 with .Net 3.5 SP1.

Upvotes: 1

Views: 5083

Answers (1)

Quartermeister
Quartermeister

Reputation: 59129

It will work the way you expect if you run it in .NET 4.0, so it looks like it was indeed a bug and has been fixed in the next release.

Upvotes: 2

Related Questions