I have a .NET Windows Form with three textbox controls: firstNameTextBox, lastNameTextBox and ageTextBox, and a simple custom class
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
and I'd like to bind the properties of an instance of Customer custom class to my Windows Forms controls. So I write:
private dynamic _customer;
private void Form_Load(object sender, EventArgs e)
{
_customer = new Customer()
{
FirstName = "Andrew",
LastName = "Chandler",
Age = 23
};
this.firstNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "FirstName"));
this.lastNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "LastName"));
this.ageTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "Age"));
}
and that works well. Then I slightly change the code by making it using an anonymous type:
private dynamic _customer;
private void Form_Load(object sender, EventArgs e)
{
_customer = new
{
FirstName = "Andrew",
LastName = "Chandler",
Age = 23
};
this.firstNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "FirstName"));
this.lastNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "LastName"));
this.ageTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "Age"));
}
and that also works rather well, although in this case I do have only one-way binding. Then I change my code even more:
private dynamic _customer;
private void Form_Load(object sender, EventArgs e)
{
_customer = new ExpandoObject();
_customer.FirstName = "Andrew";
_customer.LastName = "Chandler";
_customer.Age = 23;
this.firstNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "FirstName"));
this.lastNameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "LastName"));
this.ageTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", _customer, "Age"));
}
and I'm getting runtime error:
'Cannot bind to the property or column FirstName on the DataSource. Parameter name: dataMember'
Question: How can I bind an instance of System.Dynamic.ExpandoObject (or System.Dynamic.DynamicObject) having a set of dynamic custom properties to a Windows Forms set of (textbox) controls?
Note 1: A solution with an aggregating/container helper class would be OK for me.
Note 2: I have spent a few hours googling and trying to apply different techniques (including the ones I have found here on StackOverflow) but I have failed.
private dynamic _customer;
private void Form_Load(object sender, EventArgs e)
{
_customer = new ExpandoObject();
_customer.FirstName = "Andrew";
_customer.LastName = "Chandler";
_customer.Age = 23;
bindExpandoField(this, "FirstNameTextBox", "Text", _customer, "FirstName");
bindExpandoField(this, "lastNameTextBox", "Text", _customer, "LastName");
bindExpandoField(this, "ageTextBox", "Text", _customer, "Age");
}
private void bindExpandoField(
Control hostControl,
string targetControlName,
string targetPropertyName,
dynamic expandoObject,
string sourcePropertyName)
{
Control targetControl = hostControl.Controls[targetControlName];
var IDict = (IDictionary<string, object>)expandoObject;
var bind = new Binding(targetPropertyName, expandoObject, null);
bind.Format += (o, c) => c.Value = IDict[sourcePropertyName];
bind.Parse += (o, c) => IDict[sourcePropertyName] = c.Value;
targetControl.DataBindings.Add(bind);
}
Data binding uses Reflection, that works poorly on ExpandoObject since its "properties" are not actually properties on the underlying object. ExpandoObject implements IDictionary but that only binds easily on a list control, like ListBox.
It is not entirely impossible, you have to explicitly implement the Binding.Format and Parse events. Like this:
dynamic bag = new ExpandoObject();
bag.foo = "bar";
var bind = new Binding("Text", bag, null);
bind.Format += (o, c) => c.Value = bag.foo;
bind.Parse += (o, c) => bag.foo = c.Value;
textBox1.DataBindings.Add(bind);
That works, but of course doesn't score any elegance points.