16

我有一个包含应用程序设置的全局单例“设置”。当我尝试运行以下代码时,我得到QML CheckBox: Binding loop detected for property "checked"

CheckBox {
    checked: Settings.someSetting                         
    onCheckedChanged: {
        Settings.someSetting = checked;
    }
}

发生此错误的原因很明显,但是如何在没有绑定循环的情况下正确实现此功能?例如,我想在设置单例中保存复选框的当前选中状态。

我正在使用 Qt 5.4 和 Qml Quick 2。

问候,

4

5 回答 5

22

不要绑定它。因为复选框并不完全依赖于Setting.someSetting.

当用户单击复选框时,它CheckBox.checked会自行更改。同时,属性绑定不再有效。Settings.someSetting用户单击后无法修改复选框。因此,checked: Settings.someSetting绑定是错误的。

如果要在组件准备好时为复选框分配初始值,请使用Component.onCompleted分配它:

CheckBox {
    id: someSettingCheckBox 

    Component.onCompleted: checked = Settings.someSetting
    onCheckedChanged: Settings.someSetting = checked; 
}

如果您正在处理更复杂的场景,则Setting.someSetting可能会在运行时被其他一些东西更改,并且需要同时更改复选框的状态。捕捉onSomeSettingChanged信号并显式更改复选框。只有在程序/widget/dialog/xxx 完成时才提交someSettingCheckBoxto的值。Settings

CheckBox { id: someSettingCheckBox }

//within the Settings, or Connection, or somewhere that can get the signal.
onSomeSettingChanged: someSettingCheckBox.checked = someSetting
于 2015-01-31T13:07:58.450 回答
8

我更喜欢这个解决方案

// Within the model
Q_PROPERTY(bool someSetting READ getSomeSetting WRITE setSomeSetting NOTIFY someSettingChanged)

void SettingsModel::setSomeSetting(bool checkValue) {
    if (m_checkValue != checkValue) {
        m_checkValue = checkValue;
        emit someSettingChanged();
    }
}

// QML
CheckBox {
    checked: Settings.someSetting                         
    onCheckedChanged: Settings.someSetting = checked
}

诀窍是你emit在模型中使用 if 检查来保护它。这意味着您仍然会得到一个绑定循环,但只有一个,而不是无限的。当 if check 返回 false 从而不会发出继续循环时,它会停止。这个解决方案非常干净,您不会收到警告,但您仍然可以获得绑定的所有好处。

我想谈谈其他解决方案的局限性

CheckBox {    
    Component.onCompleted: checked = Settings.someSetting
    onCheckedChanged: Settings.someSetting = checked; 
}

在此解决方案中,您将失去绑定。它只能在创建时具有默认设置并由用户更改。如果您扩展程序以使其他事物更改模型中的值,则此特定视图将无法反映这些更改。

Settings {
    id: mySettings
    onSomeSettingChanged: checkBox.checked = someSetting
}
CheckBox {
    id: checkBox
    onCheckedChanged: mySettings.someSetting = checked
}

这个解决方案被提到来解决这些问题,但从未写出来。它功能齐全。模型更改被反映,用户可以更改数据,并且没有绑定循环,因为没有绑定;只有两个离散的分配。(x: y是绑定,x = y是赋值)

这有几个问题。首先是我认为它丑陋和不雅,但这可以说是主观的。这里看起来不错,但如果你有一个模型在这个视图中代表 10 个东西,这就会变成信号意大利面条。更大的问题是它不适用于代表,因为它们只是按需存在。

例子:

MyModel {
    id: myModel

    // How are you going to set the check box of a specific delegate when
    // the model is changed from here?
}
ListView {
    id: listView
    model: myModel.namesAndChecks
    delegate: CheckDelegate {
        id: checkDelegate
        text: modelData.name
        onCheckStateChanged: modelData.checkStatus = checked
    }
}

你实际上可以做到。我已经编写了自定义 QML 信号和连接来执行此操作,但代码复杂性让我想投掷,更糟糕的是,您可能会在不需要时强制创建委托。

于 2020-10-17T17:14:11.970 回答
5

如果您不想进行绑定循环 - 不要进行绑定,请使用代理变量,例如。其他简单的解决方案可以是检查值:

CheckBox {
    checked: Settings.someSetting                         
    onCheckedChanged: {
        if (checked !== Settings.someSetting) {
            Settings.someSetting = checked;
        }
    }
}
于 2015-01-31T10:48:35.927 回答
5

您还可以进行双向绑定来解决此问题:

CheckBox {
    id: checkBox

    Binding { target: checkBox; property: "checked"; value: Settings.someSetting }
    Binding { target: Settings; property: "someSetting"; value: checkBox.checked }
}
于 2016-11-23T12:22:43.903 回答
3

有时在控制中分离输入和输出值很有用。在这种情况下,控件始终显示实际值,并且还可以向用户显示延迟。

CheckBox {
    checked: Settings.someSetting
    onClicked: Settings.someSetting = !checked
}
于 2016-12-13T07:45:16.930 回答