Ooops…
Mike left a comment on my last post on Windows Forms Databinding asking:
What do the tests look like?
On the ComboBox binding, why aren’t you using adding the binding through DataBinding.Add? With the way you have it now if you change the value the combobox is bound too it doesn’t get pushed back to the screen.
Well Mr. Mike, on the view implementation there were no tests… *hang my head in shame* Yup, we went at it trying to understand how Windows Forms Data bindings works, but if we had gone at it test first, we would have found that leveraging the built-in data bindings are not very testable. It requires having a BindingContext setup, and in some cases the controls have to actually be displayed for the bindings to actually kick in. Second, if we had gone test first, we would have noticed the issue the Mike brought up in regards to the ComboBox.
Feeling a little guilty about publishing code that wasn’t well thought out, I decided to go at it again, with a test first approach. The test started off very high level. I knew the API that I wanted to work with, in this case a fluent interface for defining a binding to a control. The end result was quite different..
[Concern(typeof (Create))] public class when_binding_a_property_from_an_object_to_a_combo_box : context_spec { [Test] public void should_initialize_the_combo_box_with_the_current_value_of_the_property() { combo_box.SelectedItem.should_be_equal_to(baby_girl); } protected override void under_these_conditions() { combo_box = new ComboBox(); thing_to_bind_to = Dependency<IAnInterface>(); baby_girl = Dependency<IAnInterface>(); baby_boy = Dependency<IAnInterface>(); combo_box.Items.Add(baby_boy); combo_box.Items.Add(baby_girl); thing_to_bind_to .setup_result_for(t => t.Child) .Return(baby_girl); } protected override void because_of() { Create .BindingFor(thing_to_bind_to) .BindToProperty(t => t.Child) .BoundToControl(combo_box); } private ComboBox combo_box; private IAnInterface thing_to_bind_to; private IAnInterface baby_girl; private IAnInterface baby_boy; }
The end result doesn’t leverage the Windows Forms databindings at all. It registers event handlers for events on the controls.
public class ComboBoxPropertyBinding<TypeToBindTo, PropertyType> : IPropertyBinding<PropertyType> { private readonly IPropertyBinder<TypeToBindTo, PropertyType> binder; public ComboBoxPropertyBinding(ComboBox control, IPropertyBinder<TypeToBindTo, PropertyType> binder) { this.binder = binder; control.SelectedItem = binder.CurrentValue(); control.SelectedIndexChanged += delegate { binder.ChangeValueOfPropertyTo(control.SelectedItem.ConvertedTo<PropertyType>()); }; } public PropertyType CurrentValue() { return binder.CurrentValue(); } }
If you’re interested in the rest of the source code download the source here. The moral of the story… Don’t become complacent and take off your TDD hat, prematurely. In most cases it can, and should be, tested. Your design will probably come out much cleaner then going at the problem head on without tests to back you up. Not only that, but tests also give you extension points for making changes, and dealing with different contexts you probably wouldn’t have thought of right off the bat.