About data binding with custom components

Hi @stefano.menci,

Summary: You got to exactly the right solution in the end. To explain what you were seeing before, I ended up writing an in-depth explanation of data bindings. I’m including it here because it might be useful for others.

Just to restate the problem, as I understand it: You were using data-binding to set the item property on a custom component. Within that custom component, you were either binding self.item or self.item['foo'] to the text property of a TextBox (with writeback switched on).

How data bindings work

Data bindings are actually very simple. If you set the binding on the self.component_1's foo property to SOME_EXPRESSION, then when the refreshing_data_bindings event triggers on your form, we execute:

self.component_1.foo = SOME_EXPRESSION

(Setting self.item = [anything] on your form automatically triggers a data binding refresh; you can also do it manually with self.refresh_data_bindings().)

Writeback

If the foo property is enabled for writeback, then when self.component_1 raises an event that triggers that writeback (eg a CheckBox’s change event triggers writeback on its checked property), we execute this:

SOME_EXPRESSION = self.component_1.foo

Worked example

My form has self.item set to a row from the People table, which has a “name” column. I’ve got self.name_box's textbound to self.item['name'], with writeback enabled.

  1. I set self.item = person_row. This triggers an automatic data-binding refresh.
  2. The data binding runs: self.name_box.text = self.item['name']. The name appears in the text box.
  3. I now edit the text box, and click away.
  4. The lost_focus event triggers data binding writeback on the text property of a TextBox
  5. The data binding runs: self.item['name'] = self.name_box.text. This updates the database.

What happened in your original example

You had two data bindings. Inside your custom component, you’d bound self.text_box_1.text to self.item. (This is a little unconventional; traditionally self.item is dict-like, but it won’t cause any problems.) Then, outside your custom component, you bound self.custom_component_1.item to self.str_1.

  1. You set up your form. The data binding duly writes the value of self.str_1 to self.custom_component_1.item
  2. This triggers a data binding refresh within the custom component. It sets self.custom_component_1.text_box_1.text to self.custom_component_1.item
  3. You edit the textbox, and trigger data binding writeback
  4. The data binding runs: self.custom_component_1.item = self.custom_component_1.text_box_1.text.
  5. …it stops here. There is no automatic mechanism to update the outer form’s self.str_1.

Aside: Why did this work when you used dicts? Because if two variables refer to the same dict object, you can change it through one reference and view it through the other:

x = {'answer': 42}
y = x
y['answer'] = 43
print x['answer'] # Prints 43

So, if you’re setting a dict (or a database row) via data bindings, and you’re only updating keys in that dict (or columns in that DB), you don’t need writeback to make the new value available to outside code, because everyone is still looking at the same object. (You might still want an event so the outer form knows to update itself, but that’s a separate issue.)


As I said at the start, the solution you came to in the end is exactly the correct one. item is a built-in property for all custom components, and it doesn’t support writeback. If you want to trigger writeback on a property of a custom component, you need to make a new property, enable it for write-back, and then make an event that triggers write-back for that property. (You have done all this - you’ve made a property called text and an event called change that triggers write-back for it.)

6 Likes