How to make a QML toggle button that tracks/controls any boolean property
我今天进行的QML / QtQuick练习是制作一个ToggleButton小部件,我可以实例化该部件来监视指定的布尔QML属性的状态,当我单击ToggleButton时,也可以切换该属性的状态。
到目前为止,我的ToggleButton组件具有以下功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Contents of ToggleButton.qml import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 Button { property bool isActive: false onClicked: { isActive = !isActive; } style: ButtonStyle { background: Rectangle { border.width: control.activeFocus ? 2 : 1 border.color:"black" radius: 4 color: isActive ?"red" :"gray"; } } } |
....这是我的小测试工具,用于查看它是否按照我想要的方式工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | // Contents of main.qml import QtQuick 2.6 import QtQuick.Window 2.2 Window { visible: true width: 360 height: 360 Rectangle { property bool lighten: false; id:blueRect x: 32; y:32; width:64; height:64 color: lighten ?"lightBlue" :"blue"; MouseArea { anchors.fill: parent onClicked: parent.lighten = !parent.lighten; } } Rectangle { property bool lighten: false; id:greenRect x:192; y:32; width:64; height:64 color: lighten ?"lightGreen" :"green"; MouseArea { anchors.fill: parent onClicked: parent.lighten = !parent.lighten; } } ToggleButton { x:32; y:128 text:"Bright Blue Rect" isActive: blueRect.lighten } ToggleButton { x:192; y:128 text:"Bright Green Rect" isActive: greenRect.lighten } } |
您可以通过将代码分别保存到ToggleButton.qml和main.qml,然后运行" qmlscene main.qml"来运行它。
请注意,如果单击蓝色或绿色矩形,它将按预期工作;开启和关闭Rectangle对象的布尔" lighten"属性,导致Rectangle更改颜色,并且关联的ToggleButton也做出适当的反应(当" lighten"属性为true时,将自身变为红色;当" lighten"属性为true时,则变为灰色"属性为假)。
到目前为止,还算不错,但是如果您单击ToggleButton本身,则绑定已断开:也就是说,单击ToggleButton会导致ToggleButton变成预期的红色/灰色,但是矩形的颜色不符合要求,并且之后,单击矩形不再导致ToggleButton的状态发生变化。
我的问题是,正确执行此操作的诀窍是什么,以便在ToggleButton和它跟踪的属性之间始终保持双向对应? (请注意,理想的解决方案是将尽可能少的代码添加到main.qml中,因为我希望将此功能封装在ToggleButton.qml中,以最大程度地减少暴露给其余QML应用程序的复杂性)
这不起作用的原因是您正在用固定值覆盖绑定。通过在
问题是:QML现在不支持2way绑定。但是,有一些技巧可以创建一个。参见http://imaginativethinking.ca/bi-direction-data-binding-qt-quick/
注意:为什么不使用按钮的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Contents of ToggleButton.qml import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 Button { //use the"checked" property instead of your own"isActive" checkable: true style: ButtonStyle { background: Rectangle { border.width: control.activeFocus ? 2 : 1 border.color:"black" radius: 4 color: checked?"red" :"gray"; } } } |
看来解决此问题的一种方法是让ToggleButton使用别名属性而不是常规属性声明其状态。这样,只有一个外部属性(因为ToggleButton的内部属性实际上只是外部属性的别名),因此不需要双向绑定。这是按预期工作的QML代码的更新版本:
ToggleButton.qml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Contents of ToggleButton.qml import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 Button { onClicked: { isActive = !isActive; } style: ButtonStyle { background: Rectangle { border.width: control.activeFocus ? 2 : 1 border.color:"black" radius: 4 color: isActive ?"red" :"gray"; } } } |
main.qml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | // Contents of main.qml import QtQuick 2.6 import QtQuick.Window 2.2 Window { visible: true width: 360 height: 360 Rectangle { property bool lighten: false; id:blueRect x: 32; y:32; width:64; height:64 color: lighten ?"lightBlue" :"blue"; MouseArea { anchors.fill: parent onClicked: parent.lighten = !parent.lighten; } } Rectangle { property bool lighten: false; id:greenRect x:192; y:32; width:64; height:64 color: lighten ?"lightGreen" :"green"; MouseArea { anchors.fill: parent onClicked: parent.lighten = !parent.lighten; } } ToggleButton { x:32; y:128 text:"Bright Blue Rect" property alias isActive: blueRect.lighten } ToggleButton { x:192; y:128 text:"Bright Green Rect" property alias isActive: greenRect.lighten } } |