I'm having difficulty finding the idiomatic way to access ListView
's model from its delegate in QML.
Consider the following fairly simple custom delegate that supports a checkbox near the item and tracks the currently chosen item in the list. (To clarify: the checkbox selection and list selection are independent.)
Rectangle { id: wrapper required property int index required property string name required property bool selected width: ListView.view.width height: 32 radius: 3 color: "transparent" Label { anchors.left: parent.left anchors.right: selectedBox.left anchors.verticalCenter: parent.verticalCenter anchors.margins: 5 text: wrapper.name MouseArea { anchors.fill: parent onClicked: wrapper.ListView.view.currentIndex = wrapper.index } } CheckBox { id: selectedBox anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter checked: wrapper.selected onToggled: wrapper.ListView.view.model.setProperty(wrapper.index, "selected", checked) }}
This is working code though making onToggle
work was particularly troublesome. In the end I found this magical ListView.view.model
rune to access the underlying model in the Qt docs and figured out I can prepend the delegate ID to it.
It seems to be too verbose to be correct for the task delegates were created for though.
The majority of examples online hardcode this value to a particular model which violates Delegate and Model concept separation and I don't want to go this route. I also saw just model
being used (the last listing in the linked docs), but it doesn't work for me even if I require property ListModel model
for it in the delegate.
So the question is whether there is a simpler way.
Here's a sample list and model to test the delegate:
ListView { // ... model: myModel delegate: myDelegate highlight: Rectangle { color: "lightsteelblue" radius: 5 } focus: true}ListModel { id: myModel ListElement { name: "Item 1" selected: false } ListElement { name: "Item 2" selected: false }}
EDIT. There are actually two good solutions, a short one and a more verbose but foolproof.
I could drop all required property declarations and use
Label { // ... text: name // ... } CheckBox { // ... checked: selected onToggled: selected = checked }
I couldn't come to this solution because my initial design had the model property named checked
and then it's kind of unclear how this approach can be applied then.
In case of name collisions it's possible to make a required model
property and then it's bound to the corresponding row of the list model:
Rectangle { id: wrapper required property var model // ... Label { // ... text: model.name // ... } CheckBox { // .. checked: model.selected onToggled: model.selected = checked }}
The second options seems to be preferable, so I'll go with it.