Подтвердить что ты не робот

QML: Как передать свойства модели делегату, загруженному внутри делегата GridView (или ListView)?

Я пытаюсь создать повторно используемый компонент QML, который внутренне размещает GridView. Каждый элемент в GridView имеет набор поведений (в основном, для отображения и на основе мыши), которые являются общими для всего приложения. Однако то, что отображается внутри элементов GridView, изменяется в зависимости от варианта использования. (То есть тот же инкапсулированный компонент для одного GridView, но в другом месте приложения он может использовать другой компонент.)

Итак, я бы хотел, чтобы каждый вызов снабжал делегатом, который добавляется к каждому элементу в GridView, который уже является делегатом. Другими словами, что-то вроде этого:

MyGrid.qml

import QtQuick 1.1

Rectangle {
  id: myGrid
  objectName: "myGrid"

  property Component internalDelegate
  property variant internalModel

  GridView {
    anchors.fill: parent

    delegate: Row {
      Loader {
        sourceComponent: myGrid.internalDelegate
      }
    }

    model: parent.internalModel
  }
}

Идея заключается в том, что загрузчик внутри делегата GridView загружает предоставленный пользователем делегат, который будет выглядеть примерно так:

Main.qml

import QtQuick 1.1

Rectangle {
  anchors.fill: parent

  width: 300
  height: 200

  MyGrid {
    anchors.fill: parent

    internalDelegate: Text {
      text: name
    }

    internalModel: ListModel {
      ListElement {
        name: "a"
      }

      ListElement {
        name: "b"
      }
    }
  }
}

Однако это не работает. QML сообщает, что "имя" является неизвестной переменной внутри элемента "Текст". Если я заменю переменную имени на строку (т.е. "Привет" ), она будет работать как ожидалось.

Мой вопрос в том, как передать переменную "имя" в innerDelegate, или, еще лучше, сделать всю модель доступной, так что internalDelegate может получить доступ ко всем из них (так как вызывающая сторона также определяет модель).

Вопрос: есть ли лучший способ сделать это?

4b9b3361

Ответ 1

Я решил это так:

MyGrid.qml

import QtQuick 1.1

Rectangle {
    id: myGrid
    objectName: "myGrid"

    property Component internalDelegate
    property variant internalModel

    GridView {
        id: grid
        anchors.fill: parent

        delegate: Row {
            Loader {
                sourceComponent: myGrid.internalDelegate
                property variant modelData: grid.model.get(index)
            }
        }

        model: parent.internalModel
    }
}

main.qml   импортировать QtQuick 1.1

Rectangle {
    anchors.fill: parent

    width: 300
    height: 200

    MyGrid {
        anchors.fill: parent

        internalDelegate: Text {
            text: modelData.name
        }

        internalModel: ListModel {
            ListElement {
                name: "a"
            }

            ListElement {
                name: "b"
            }
        }
    }
}

Не знаю, лучше ли это, но у него есть меньше и проще код. Модели списка немного неудобны и не соответствуют действительности в QML. И имейте в виду, что model.get(index) принадлежит GridView, потому что вся модель принадлежит этому. Поэтому, если вы хотите использовать этот объект после уничтожения списка, вам нужно его повторно или скопировать.

Ответ 2

Просто хочу указать, что "grid.model.get(model.index)" здесь:

    delegate: Row {
        Loader {
            sourceComponent: myGrid.internalDelegate
            property QtObject modelData: grid.model.get(index) //<
        }
    }

Является эквивалентом "модели" в текущем контексте:

    delegate: Row {
        Loader {
            sourceComponent: myGrid.internalDelegate
            property QtObject modelData: model //< 
        }
    }

Ответ 3

Я отвечаю на свой вопрос, потому что продолжал работать над проблемой и нашел приемлемое решение. Я не отмечаю этот вопрос, как ответ (пока), потому что я хотел бы знать, есть ли более простые или более эффективные решения.

Вот как я решил эту проблему: я использовал компонент Binding с загрузчиком и создал пользовательский компонент MyDelegate, который имеет свойство варианта для текущего текущего элемента ListElement. Здесь измененный код с важными изменениями, отмеченными комментариями.

Main.qml

import QtQuick 1.1

Rectangle {
  anchors.fill: parent

  width: 300
  height: 200

  MyGrid {
    anchors.fill: parent

    // *** Use MyDelegate rather than generic Component and refer to
    // *** model properties via MyDelegate model property
    internalDelegate: MyDelegate {
      Text {
        text: model.index + model.name
      }
    }

    internalModel: ListModel {
      ListElement {
        name: "a"
      }

      ListElement {
        name: "b"
      }
    }
  }
}

MyGrid.qml

import QtQuick 1.1

Rectangle {
  id: myGrid
  objectName: "myGrid"

  property Component internalDelegate
  property variant internalModel

  GridView {
    anchors.fill: parent

    delegate: Row {
      Loader {
        id: loader

        sourceComponent: myGrid.internalDelegate

        // *** Bind current model element to the component
        // *** when it loaded
        Binding {
          target: loader.item
          property: "model"
          value: model
          when: loader.status == Loader.Ready
        }
      }
    }

    model: parent.internalModel
  }
}

Важным дополнением является пользовательский компонент MyDelegate, который определяет свойство модели как вариант. Это связывается во время интерпретации, то есть нет ошибки в Main.qml, когда ссылается на свойство имени модели.

MyDelegate.qml

import QtQuick 1.1

Rectangle {
  property variant model
}

Что-то подсказывает мне еще более простой способ сделать это, но сейчас это работает.