Two way binding C++ model in QML
Twoway binding is a complicated matter in QML, as it usually works as some assignment.
So, if you bind a property with propertyname: valuetobeboundto
and later assign something to propertyname
again, this binding will be lost.
As a workaround there are two ways: The use of Binding
-Objects or to not use binding, but handle all the property-change-signals (which your model ideally properly emits) manually.
For the first, you can find a detailed instruction here.
Here they use the one Binding
-Object for each direction. The good thing is, those Binding
s will not be overridden, by assignment of a new Binding
.
Consider:
Row {
spacing: 2
Rectangle {
id: r0
width: 50
height: 30
}
Rectangle {
id: r1
width: 50
height: 30
color: b2.pressed ? 'red' : 'blue'
}
Button {
id: b2
}
Button {
id: b3
onPressed: r1.color = 'black'
onReleased: r1.color = 'green'
}
Binding {
target: r0
property: 'color'
value: b2.pressed ? 'red' : 'blue'
}
Binding {
target: r0
property: 'color'
value: (b3.pressed ? 'black' : 'green')
}
}
At the beginning the value of r1
is bound to the state of b2
, but as soon as b3
has been pressed once, r1
won't be updated by a click on b2
anymore. For r0
the updating will be done by the two Binding
-Objects, and therefore the Binding
won't be lost. However, you can see, how the binding works: When ever the state of the Button
changes, the Binding
will be updated.
So the press AND the release of b2
will fire signals, that will be handled by the first Binding
and the same goes for the press AND relase of b3
.
Now coming to the two-way binding. Here it is important to avoid Binding-Loops.
Row {
Button {
id: count0
property int count: 0
onClicked: count += 1
text: count
}
Button {
id: count1
property int count: 0
onClicked: count += 1
text: count
}
Binding {
target: count0
property: 'count'
value: count1.count
}
Binding {
target: count1
property: 'count'
value: count0.count
}
}
While this example is perfectly fine. The changing of count0.count
will trigger a change of count1.count
. Now it is checked, if count0.count
would need an update, but the value is already the right, so the recursion ends, and no binding-loop occures.
Changing the second Binding to
Binding {
target: count1
property: 'count'
value: count0.count + 1
}
drastically changes the situation: Now with each change of count0.count
, count1.count
needs to be raised. The first Binding
then tries to set count0.count
to the same value as count1.count
but there is just no way that both Binding
will be satisfied, and no change is needed to be done, after the other Binding
did it's work. It will result in a binding-loop. Luckily those are detected pretty fine in QML, so a lock is avoided.
Now there is only one last thing to take care of: Consider this Component-Definition:
// TestObj.qml
Item {
width: 150
height: 40
property alias color: rect.color
Row {
spacing: 10
Rectangle {
id: rect
width: 40
height: 40
radius: 20
color: butt.pressed ? 'green' : 'red'
}
Button {
id: butt
text: 'toggle'
}
}
}
Here we have an internal binding of the color
-property, by using the propertyname: valueToBeBoundTo
-Syntax. This means, the internal binding might be overwritten by any external assignemtn of the color
-property.
Replace this binding by a Binding
-Object, and you should be fine.
The same would go the other way around: color
is externally bound to some value, and then you handle a signal internally and assign a value to it, the external binding would be lost, if not created by a Binding
-Object.
This is only a general overview. There are way more details that might alter the behavior of Binding. But I think I have shown, how you can create a Two-Way-Binding and mentioned quite some pitfalls, you might encounter.
This works for me, with Qt Quick Controls 2:
TextField {
id: txt
text: testMessage.message
onTextChanged: testMesage.message = txt.text
}