I’m in the midst of testing a user control I’ve built, and I’m encountering something that’s inexplicable to me.
The control’s an extension of the ComboBox that handles values of a specific custom type. It has a dependency property of that custom type that is the target property of a Binding.
I’ve got a trace statement in the setter, and I can see that the property is getting set. But it’s not appearing in my user control.
Now, ordinarily I’d say, okay, I’ve got a bug in my user control. I probably do, though I’m baffled about it. But this question isn’t about finding the bug in my control. Read on; here is where it gets weird.
I’m also using Bea Stollnitz’s little value converter to help debug the Binding:
public class DebuggingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value; // Add the breakpoint here!!
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This method should never be called");
}
}
The idea behind this is that I add this converter to my Binding and can set a breakpoint to see what value is being pushed out to the target. Okay, that works just fine. I can see that the value is being pushed out.
In fact, it works a little too fine. If the DebuggingConverter is attached to the Binding, the user control displays the value. If it’s not, it doesn’t.
How is that even possible? How could a value converter that does nothing affect the behavior of a bound control?
Edit:
Not that it’s likely to help, but here’s the XAML for the user control:
<a:CodeLookupBox
Grid.Column="1"
Grid.IsSharedSizeScope="True"
MinWidth="100"
Style="{Binding Style}">
<a:CodeLookupBox.CodeLookupTable>
<Binding Path="Codes" Mode="OneWay"/>
</a:CodeLookupBox.CodeLookupTable>
<a:CodeLookupBox.SelectedCode>
<Binding Path="Value" Mode="TwoWay" ValidatesOnDataErrors="True"/>
</a:CodeLookupBox.SelectedCode>
</a:CodeLookupBox>
Without the converter on the second binding, the control behaves as though I didn’t set SelectedCode. Even though a trace statement in the OnSelectedCodePropertyChanged handler shows that e.Value does indeed contain the correct value. This happens irrespective of whether the converter’s attached or not.
I’ve been trying to reverse-engineer this problem with a thought experiment: if you wanted to create a bound user control whose behavior changed if a no-op converter were attached to its binding, how would you do it? I don’t know enough about binding to come up with an answer.
Well, the good news is, I know why
SelectedCodeisn’t being set when I’m not using a value converter. The bad news is, I still have something of a mystery, but the problem’s been pushed up the food chain a bit, and I have a workaround.This control is essentially a strongly-typed combo box with a bunch of additional features that are made possible by the fact that it knows what kind of items are in it. The
SelectedCodeandCodeLookupTableproperties are strongly typed, and they hide the underlyingSelectedItemandItemsSourceproperties, which aren’t. (This, by the way, is why this is a user control and not a subclass ofComboBox; I don’t want those properties to be visible because a lot of things can happen if they get set improperly, none of them good.)Here’s what’s happening. This is my debugging output when the value converter is attached (the number is the hash code of the control, because I’ve got a bunch of them that all get drawn simultaneously when the program’s initialized):
This is the expected behavior. The
CodeLookupTableproperty is set, so settingSelectedCodeto one of the items in that collection correctly setsSelectedItemon the underlyingComboBox.But without the value converter, we get this:
Here, the
SelectedCodeproperty is being set before theCodeLookupTableproperty is. So when the method tries to setSelectedItemon the underlyingComboBox, nothing happens, because theItemsSourceis null.And here is the root of the problem. I’ve foolishly assumed that the order that bindings update their target in is the same as the order they’re declared in the XAML. (One of the reasons I’ve expressed the bindings as elements instead of attributes is because the order of elements in an XML document is deterministic and the order of attributes isn’t. It’s not like I didn’t think about this.) This is apparently not the case.
I’ve also assumed, maybe a little less foolishly, that the order in which bindings update their target isn’t dependent on whether or not they have attached value converters. Well, it is. I wonder what else it depends on.
Mercifully, I have a way to work around this. Since my
CodeLookupobject contains a reference to theCodeLookupTable, I can make theSelectedCodesetter set theCodeLookupTable(and thus theItemsSource) property first, if it hasn’t already been set. That’ll make this problem go away without having to stick a fake value converter on the binding and hope that the way bindings behave never changes.Edit
Here’s what the property declarations look like: