通过另一个转发器中的复选框控制转发器中的项目 "enabled" 属性

Controling an Item "enabled" property in a repeater from a checkbox in another repeater

这听起来很容易,但它并没有像我预期的那样工作。

我有一个带复选框的中继器,垂直放置。 然后我有一个组合框网格。 我希望在选中相应的复选框时禁用组合框。

这是代码的摘录:

Repeater {
    id: idChkUseChordNotes
    model: _max_patterns

    SmallCheckBox {
        onClicked: {
            console.log("Clicked at " + index + "!! ==> " + idChkUseChordNotes.itemAt(index).checked);
            // attempt to force a refresh of the idStepNotes repeater => No effect
            //resetP=false;
            //resetP=true;
        }
    }
}

Repeater {
    id: idStepNotes
    model: getPatterns(resetP)

    StackLayout {
        width: parent.width
        currentIndex: modeIndex()

        property int stepIndex: index % _max_steps
        property int patternIndex: Math.floor(index / _max_steps)

        Label {
            text: "dummy"
        }

        ComboBox {
            id: lstGStep
            property var step: patterns[patternIndex * _max_steps + stepIndex]
            editable: false
            enabled: !idChkUseChordNotes.itemAt(patternIndex).checked
            model: _ddGridNotes
            currentIndex: find(step.degree, Qt.MatchExactly)
            onCurrentIndexChanged: {
                step.degree = model[currentIndex];
                // debug
                console.log(idChkUseChordNotes.itemAt(patternIndex).checked); // <-- working fine
            }
        }
    }
}

虽然组合框可以访问正确的复选框(请参阅“onCurrentIndexChanged”中的“调试”),但选中和关闭复选框不会影响组合框 属性。

我知道 QML 在 属性 绑定级别上有一些限制。我不知道这是否是这里的问题。

重要提示:我仅限于这些版本:QtQuick 2.9 和 QtQuick.Controls 2.2

您应该会看到一堆这样的警告:TypeError: Cannot read property 'checked' of null。令人惊讶的是(根据您的评论)您没有。在绑定被评估时,复选框还没有被实例化。解决这个问题需要各种 Component.onCompleted 处理程序中的程序代码,正如您已经在评论中指出的那样。但是,这不是 QML 的预期使用方式。

与其根据 UI 元素相互作用来考虑问题,不如考虑更 data-centric 的方法。复选框和组合框实际上只是同一组数据的不同视图或方面。每个数据点都有一个布尔方面(或 Qt 术语中的 属性)“使用和弦音符”和另一个方面(“程度”?)具有另一种含义。复选框是布尔方面的视觉和可交互表示,组合框对另一个方面做同样的事情,但使用第一个来决定交互性。

在代码中表示这种结构的 Qt/QML 方法是通过视图、绑定和模型。我已经模拟了一个示例,说明如何使用您的代码段作为起点。请注意这里没有声明性代码(按钮的 onClicked 处理程序除外,但那只是为了说明目的)。

有几点需要注意:

  • 为了简单起见,这里的模型是一个QML ListModel。在 real-world 应用程序中,这很可能是用 C++ 实现的。
  • 复选框和组合框都使用 two-way 绑定来确保它们的状态始终与模型 up-to-date 一致,即使模型由于其他原因发生更改也是如此。第三列中的按钮说明了这一点。如果不需要这个,Binding 对象可以用简单的绑定表达式替换:checked: useChordNotes.
  • combobox上的currentIndex绑定使用了这里作为模型的JS数组的indexOf方法。在您的代码中,必须将其替换为适用于 _ddGridNotes 具有的任何类型的内容。 ComboBox.find() 在这里不起作用,因为初始化顺序。在 Binding 构造后第一次被评估的那一刻,组合框尚未实例化其项目,因此对于任何对 find 的调用总是返回 -1。这与您原来的问题类似。

代码

import QtQuick 2.9
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.2
import QtQuick.Window 2.9

Window {
    width: 640
    height: 480
    visible: true

    ListModel {
        id: patterns

        ListElement {
            useChordNotes: false
            gridNote: "1"
        }

        ListElement {
            useChordNotes: true
            gridNote: "3"
        }

        ListElement {
            useChordNotes: false
            gridNote: "2"
        }
    }

    RowLayout {
        anchors.fill: parent

        ColumnLayout {
            Repeater {
                id: idChkUseChordNotes

                model: patterns

                CheckBox {
                    // Use a two-way binding here, in case something else in the program changes the model (e.g. loading a file)

                    // #1: change model value on user interaction
                    onCheckedChanged: useChordNotes = checked

                    // #2: update value on model change from another source
                    Binding on checked {
                        value: useChordNotes
                    }
                }
            }
        }

        ColumnLayout {
            Repeater {
                id: idStepNotes

                model: patterns

                ComboBox {
                    id: lstGStep

                    enabled: !useChordNotes
                    model: ["1", "2", "3"]

                    onActivated: gridNote = currentValue

                    Binding on currentIndex {
                        // Replace this to something that works with whatever type lstGStep.model might be
                        value: lstGStep.model.indexOf(gridNote)
                    }
                }
            }
        }

        ColumnLayout {
            Repeater {
                model: patterns

                Button {
                    text: "useChordNotes: " + (useChordNotes ? "true" : "false") + "; gridNote: " + gridNote
                    onClicked: {
                        gridNote = "3";
                        useChordNotes = !useChordNotes;
                    }
                }
            }
        }
    }
}