Различные делегаты для QML ListView

Я хотел бы знать, можно ли использовать (несколько) разных делегатов для QML ListView.

В зависимости от отдельного объекта в модели ListView я хотел бы визуализировать объекты с разными делегатами.

Этот фрагмент кода объясняет, чего я хочу достичь:

main.qml

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2

ApplicationWindow {
    title: qsTr("Hello World")
    width: 640
    height: 480
    visible: true

    ListModel {
        id: contactsModel
        ListElement {
            name: "Bill Smith"
            position: "Engineer"
        }
        ListElement {
            name: "John Brown"
            position: "Engineer"
        }
        ListElement {
            name: "Sam Wise"
            position: "Manager"
        }
    }

    ListView {
        id: contactsView
        anchors.left: parent.left
        anchors.top: parent.top
        width: parent.width
        height: parent.height
        orientation: Qt.Vertical
        spacing: 10
        model: contactsModel
        delegate: {
            if (position == "Engineer") return Employee;  //<--- depending on condition, load Contact{}
            else if (position == "Manager") return Manager; //<--- depending on condition, load Person{}
        }
    }
}

Employee.qml (Один из возможных компонентов, который я хотел бы использовать в качестве делегата)

import QtQuick 2.4

Rectangle{
    width: 200
    height: 50
    color: ListView.isCurrentItem ? "#003366" : "#585858"
    border.color: "gray"
    border.width: 1

    Text{
        anchors.centerIn: parent
        color: "white"
        text: name
    }
}

Manager.qml (другой компонент, который я хотел бы использовать в качестве делегата)

import QtQuick 2.4

Rectangle{
    width: 200
    height: 50
    color: "red"
    border.color: "blue"
    border.width: 1

    Text{
        anchors.centerIn: parent
        color: "white"
        text: name
    }
}

Буду признателен за любой совет! Спасибо!

Ответ 1

Я считаю, что было бы лучше реализовать один базовый делегат для всех типов position, который загружает конкретную реализацию в зависимости от position или любых других свойств данных, используя Loader

BaseDelegate {
    property var position

    Loader {
        sourceComponent: {
            switch(position) {
                case "Engineer": return engineerDelegate
            }
        }
    }

    Component {
        id: engineerDelegate
        Rectangle {
             Text {  }
        }
    }
}

Ответ 2

У меня была такая же проблема, документация Qt дает довольно хороший ответ: http://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate

Самое простое решение - встроенный Component с Loader для установки файла source:

ListView {
    id: contactsView
    anchors.left: parent.left
    anchors.top: parent.top
    width: parent.width
    height: parent.height
    orientation: Qt.Vertical
    spacing: 10
    model: contactsModel
    delegate: Compenent {
        Loader {
            source: switch(position) {
                case "Engineer": return "Employee.qml"
                case "Manager": return "Manager.qml"
            }
        }
    }
}

Любая попытка использовать Loader.srcComponent приведет к отсутствию любой переменной из модели (включая index). Единственный способ присутствия переменных - дети Component находиться внутри основного Component, но тогда может присутствовать только один, поэтому он бесполезен.

Ответ 3

Я реализовал его следующим образом:

ListView {
    id: iranCitiesList
    model: sampleModel
    delegate: Loader {
        height: childrenRect.height
        width: parent.width
        sourceComponent: {
            switch(itemType) {
            case "image" :
                return imageDel;
            case "video":
                return videoDel;
            }
        }
    }
    ImageDelegate { id: imageDel }
    VideoDelegate { id: videoDel }
}


ImageDelegate.qml

Component {
    Image { /*...*/ }
}


VideoDelegate.qml

Component {
    Item { /*....*/ }
}

В последнем случае проверьте ширину и высоту делегатов. В моем случае мне пришлось снова установить ширину и высоту моего делегата в Loader.
Удачи - Мусави

Ответ 4

Поскольку у вас есть только два типа, следующий код так же прост в обслуживании, как легко понять:

delegate: Item {
    Employee { visible = position === "Engineer" }
    Manager { visible = position === "Manager" }
}

В случае, если число типов будет расти, это не подходящее решение, которое легко приводит к утверждению ада.

Ответ 5

Конечно, это возможно. ListView.delegate - это своего рода указатель на Component, который будет рисовать элементы, чтобы вы могли его изменить.

Например:

Employee { id: delegateEmployee }
Manager { id: delegateManager}
...
ListView {  
    property string position   
    delegate: position == "Engineer" ? delegateEmployee : delegateManager
}