views:

40

answers:

1

Something is wrong with my binding. But I can't find it

I have a status type control (UserControl) that has an ItemsControl with binding that relies on a ViewModelBase object which provides list of BrokenRules, like so:

<ItemsControl ItemsSource="{Binding BrokenRules}" >
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <Hyperlink Foreground="Red" >
                    <TextBlock Text="{Binding Description}" />
                </Hyperlink>
            </TextBlock>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The binding works the way I want to, in the sense that any and all broken rules Descriptions are displayed. A rule is pretty much just a description and a delegate that is executed when the rule is told to validate itself.

Most rules have Descriptions that are known up front, before the rule is asked to validate itself. For example, "Name is not valued" is a fine description of what went wrong if the validation delegate !Name.IsNullOrEmptyAfterTrim() fails.

The problem comes with one particular rule, which checks for duplicate names. If the dup check fails, I want to be able to say what the duplicated value is, which is impossible to know up front. So the rule needs to update the Description when the validation delegate is executed.

When I unit test or leave a debug trace in the validation delegate, the broken rule description is updated. But when I run the app, the broken rule description is what is was before it was updated.

I am therefore guessing my binding is not correct. Can anyone suggest what the problem / fix is?

Cheers,
Berryl

UPDATE ====================

This is code from my ViewModelBase class:

private readonly List<RuleBase> _rules = new List<RuleBase>();

// inheritors add rules as part of construction
protected void _AddRule(RuleBase rule) { _rules.Add(rule); }

public ObservableCollection<RuleBase> BrokenRules { get { return _brokenRules; } }
protected ObservableCollection<RuleBase> _brokenRules;

public virtual IEnumerable<RuleBase> GetBrokenRules()         {
        return GetBrokenRules(string.Empty);
}

public virtual IEnumerable<RuleBase> GetBrokenRules(string property)        {
    property = property.CleanString();

     _brokenRules = new ObservableCollection<RuleBase>();
    foreach (var r in _rules)            {
        // Ensure we only validate this rule 
        if (r.PropertyName != property && property != string.Empty) continue;

        var isRuleBroken = !r.ValidateRule(this);

        if (isRuleBroken) _brokenRules.Add(r);

        return _brokenRules;
    }
+1  A: 

You must ensure that the BrokenRules observable collection instance doesn't change, your code on the View Model should look something like:

public ObservableCollection<BrokenRule> BrokenRules
{
  get;
  set;
}

private void ValidateRules()
{
  // Validation code
  if (!rule.IsValid)
  {
    this.BrokenRules.Add(new BrokenRule { Description = "Duplicated name found" });
  }
}

If for example, you do something like this instead:

this.BrokenRules = this.ValidateRules();

You would be changing the collection which is bound to the ItemsControl without notifying it and changes won't reflect on UI.

Anero
I think your close to it. My list of broken rules wasn't an ObservableCollection AND it creates a new instance in each loop. I fixed the collection type, since that was trivial, and I will take a look at how to do this with the same collection instance next. You need to remove rules that become unbroken as well as add broken ones so it isn't as trivial though. Cheers
Berryl
@Berryl: Instead of creating a new collection, you can use `.Clear()`. This will not break the data binding.
Jens
The combination of all the comments here got this working correctly. Since Anero had the only thing I could mark as an answer, I marked his as such. Thanks all!
Berryl