Мне трудно найти идиоматический способ доступа к модели ListView из ее делегата в QML.
Рассмотрим следующий довольно простой пользовательский делегат, который поддерживает флажок рядом с элементом и отслеживает текущий выбранный элемент в списке. (Для пояснения: выбор флажка и выбор списка независимы.)
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)
}
}
Это рабочий код, хотя заставить onToggle работать было особенно сложно. В конце концов я нашел эту волшебную ListView.view.model руну для доступа к базовой модели в документации Qt и понял, что могу добавить к ней идентификатор делегата.
Однако это кажется слишком многословным, чтобы быть правильным для задач, для которых были созданы делегаты.
Большинство онлайн-примеров жестко запрограммировали это значение для конкретной модели, что нарушает разделение концепций делегата и модели, и я не хочу идти по этому пути. Я также видел, как используется только model (последний список в связанных документах), но у меня это не работает, даже если я require property ListModel model для этого в делегате.
Итак, вопрос в том, есть ли более простой способ.
Вот пример списка и модели для тестирования делегата:
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
}
}
РЕДАКТИРОВАТЬ. На самом деле есть два хороших решения: короткое и более многословное, но надежное.
Я мог бы отказаться от всех необходимых объявлений свойств и использовать
Label {
// ...
text: name
// ...
}
CheckBox {
// ...
checked: selected
onToggled: selected = checked
}
Я не смог прийти к этому решению, потому что в моем первоначальном проекте было свойство модели с именем checked, и тогда было непонятно, как этот подход можно применить.
В случае конфликта имен можно создать обязательное свойство model, а затем привязать его к соответствующей строке модели списка:
Rectangle {
id: wrapper
required property var model
// ...
Label {
// ...
text: model.name
// ...
}
CheckBox {
// ..
checked: model.selected
onToggled: model.selected = checked
}
}
Второй вариант кажется предпочтительнее, поэтому я выберу его.





Существует несколько способов получить доступ к модели из делегата. В основном это зависит от типа модели, которую использует ListView.
Обычно я предпочитаю явно отмечать свойство модели как необходимое, используя required property var model, особенно если вы определяете другие свойства по мере необходимости. В противном случае вы можете опустить это.
Например, в вашем случае вы можете определить делегата следующим образом:
Component {
id: myDelegate
CheckDelegate { // Need QtQuick.Controls
required property var model
width: ListView.view.width
height: 32
text: model.name
checked = model.selected
onCheckedChanged: model.selected = checked
Timer { // Emulating an external entity that update the model
interval: 4000
onTriggered: parent.model.selected = !parent.model.selected
running: true
}
}
}
В этом случае я инициализирую и связываю свойство checked с model.selected. Когда подается сигнал onCheckedChanged, я тоже обновляю модель.
Если внешний объект (таймер в предыдущем примере) изменит значение модели, проверенное состояние также будет обновлено.
Плохая часть этого подхода — повторение привязок:
onCheckedChanged.model.selected.selected.CheckDelegate.checked оценивает свойство model.selected из-за привязки, но в этом случае они равны, и никакие другие сигналы выдаваться не будут.Пункт 4 не нужен, но он возникает из-за реализации CheckBox/CehckDelegate.
Лучший подход должен быть следующим: пользователи нажимают на делегата, генерируется сигнал щелчка и проверенное состояние не обновляется. Сигнал обновляет значение модели, которое, благодаря привязке, также обновляет проверяемое свойство.
Чтобы получить это, вам необходимо реализовать свой собственный элемент управления.
Могу только пожаловаться, что Qt выбрал для этого model свойства довольно запутанное имя. Я думал, что это относится ко всей модели списка, но, видимо, это только одна его строка. Да...
Я согласен с вами. Например, если вы используете ComboBox в качестве делегата, у него уже есть свойство с именем model. В данном случае я определяю новое свойство property var modelItem: ListView.view.model.get(index) (если вы используете ListModel)
Сигнал, излучаемый при взаимодействии с пользователем: onToggled
Хорошо, но после сигнала onToggled элемент управления принудительно меняет свое проверенное состояние. Реализация по умолчанию не позволяет вам управлять запросами переключения.
Я думаю, ты поступаешь жестко. При использовании ListElements вы можете напрямую использовать его свойства. Нет необходимости использовать ListView.view.model. Вам больше не нужны эти необходимые свойства. Если я попытаюсь заставить ваш пример делегата работать, это будет примерно так:
Rectangle {
id: wrapper
property int selectedIndex: ListView.view.currentIndex
onSelectedIndexChanged: {
selected = selectedIndex === index
}
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: name
}
CheckBox {
id: selectedBox
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: selected
}
MouseArea {
anchors.fill: parent
onClicked: {
wrapper.ListView.view.currentIndex = index
}
}
}
Я использовал обязательные свойства в документации Qt «В большинстве случаев вам следует использовать обязательные свойства для передачи данных модели вашим делегатам». Я попробовал ваш подход, но «проверено: выбрано» в CheckBox не работает должным образом: после включения флажка базовая модель не обновляется. (Я проверяю это, записывая в console.info всю модель.)
Кроме того, способ перемещения MouseArea из Label блокирует ввод с помощью мыши флажков. Однако они по-прежнему доступны через порядок табуляции и пробел.
Я не получил роль с selectedIndex, которую ты представил. Мне нужно, чтобы selected был привязан к флажку, а не к выбранной записи списка.
Хорошо, если я добавлю onToggled: selected = checked, модель начнет обновляться. Спасибо! Родина тебя не забудет!
Хммм, это вроде сработало. Я не могу использовать
CheckDelegate, так как визуальная подсказка для выбранного в данный момент элемента списка потеряна, но привязкаmodelсвойства и назначениеmodel.selectedпри переключении флажка работает.