QML: Как я могу передать свойства модели делегату, загруженному внутри делегата GridView (или ListView)? - PullRequest
15 голосов
/ 09 декабря 2011

Я пытаюсь создать повторно используемый компонент 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 сообщает, что name является неизвестной переменной внутри элемента Text.Если я заменяю переменную name строкой (то есть «hello»), она работает как положено.

У меня вопрос, как я могу передать переменную «name» в internalDelegate, или, что еще лучше, сделать весьМодель доступна, так что internalDelegate может получить доступ ко всем из них (поскольку вызывающая сторона также определяет модель).

Дополнительный вопрос: есть ли лучший способ сделать это?

Ответы [ 3 ]

14 голосов
/ 10 декабря 2011

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

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

import 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, потому что им владеет вся модель.Поэтому, если вы хотите использовать этот объект после того, как список был уничтожен, вам нужно переписать или скопировать его.

12 голосов
/ 20 октября 2012

Просто хочу указать, что "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 //< 
        }
    }
4 голосов
/ 10 декабря 2011

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

Вот как я решил эту проблему: я использовал компонент Binding с Loader и создалпользовательский компонент 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's properties via MyDelegate's 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's 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
}

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

...