如何制作 tracks/controls 任何布尔值 属性 的 QML 切换按钮

How to make a QML toggle button that tracks/controls any boolean property

我今天的 QML/QtQuick 练习是制作一个 ToggleButton 小部件,我可以实例化它来监视指定布尔 QML 属性 的状态,并且还可以切换 属性当我点击 ToggleButton 时的状态。

到目前为止,我的 ToggleButton 组件有这个:

// 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";
        }
    }
}

.....这是我用来查看它是否按我想要的方式工作的小测试工具:

// 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,然后 运行ning "qmlscene main.qml".

请注意,如果您单击蓝色或绿色矩形,它会按预期工作; Rectangle 对象的布尔值 "lighten" 属性 打开和关闭,导致 Rectangle 改变颜色,关联的 ToggleButton 也会做出适当的反应(当 "lighten" 属性 为真,当 "lighten" 属性 为假时为灰色)。

到目前为止一切顺利,但是如果您随后单击 ToggleButton 本身,绑定将被破坏:也就是说,单击 ToggleButton 会导致 ToggleButton 按预期转动 red/gray,但矩形的颜色没有效仿,在这样做之后,单击矩形也不再导致 ToggleButton 的状态发生变化。

我的问题是,正确执行此操作的诀窍是什么,以便我始终在 ToggleButton 和它正在跟踪的 属性 之间建立双向对应关系? (请注意,理想的解决方案是向 main.qml 添加尽可能少的代码,因为我希望将此功能封装在 ToggleButton.qml 中,以最大程度地减少暴露给 QML 其余部分的复杂性应用)

这不起作用的原因是您正在用固定值覆盖绑定。通过在您 onClicked 中手动分配一个值,您可以用一个值覆盖绑定。

问题是:QML 目前不支持 2way 绑定。但是,有一些技巧可以创建一个。参见 http://imaginativethinking.ca/bi-directional-data-binding-qt-quick/

注意:为什么不使用按钮的选中状态,而不是像这样使用isActive 属性? (来自documentation)这样绑定就不会中断,即使你点击按钮:

// 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:

// 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:

// 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
   }
}