views:

91

answers:

1

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.

+1  A: 

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.

Quartermeister
Thanks. Nice to know I'm not going insane and that it's fixed in 4.0. I've worked around it by wrapping calls to `BindingGroup.ValidateWithoutUpdate()` to catch the ArgumentException and treating it as a failed validation. Annoying, but workable until I'm allowed to migrate to 4.0.
Nathan Ernst