如何创建可重用的QML对象,使其能够注入另一个QML对象?

6

如何创建可重用的QML对象,该对象能够注入另一个对象?

我曾尝试使用ComponentLoader,但似乎不是我想要的。(它仍然封装了整个QML类型,缺乏弹性,难以重复使用)

使用示例:

Card.qml

import QtQuick 2.0
import QtQuick.Layouts 1.3

Rectangle {
    default property var innerObject
    property string titleText: "[Hello Untitled Title]"
    id: root
    color: "#fff"
    ColumnLayout {
        anchors.fill: parent
        Rectangle {
            id: header
            height: 10
            width: parent.width
            color: "#666"
            RowLayout {
                Text { text: titleText; color: "#fff" }
            }
        }

        // How to inject innerObject in here ?

    }
}

main.qml

import QtQuick 2.0
import QtQuick.Layouts 1.3

Card {
    titleText: "Image Information"
    ColumnLayout { /* .......*/ }   // innerObject
}

Card {
    titleText: "Image Viewer"
    Rectangle { /* .......*/ }      // innerObject
}

3
可能是[如何在QML中将一个QML Item分配给组件属性,然后在组件内使用该对象?]的重复问题。(链接已提供) - Teemu Risikko
请特别注意:https://dev59.com/Hm445IYBdhLWcg3wIG19#10031214 - Teemu Risikko
3个回答

4

The answer I linked works like this:

Main.qml

Card {
    titleText: "Image Viewer"
    innerObject: Rectangle {
        Component.onCompleted: {
            console.log(parent.objectName)
        }
    }
}

Card.qml

Rectangle {
    property string titleText: "[Hello Untitled Title]"

    default property alias innerObject : innercolumn.children


    id: root
    color: "#fff"
    ColumnLayout {
        id: innercolumn
        objectName: "column"
        anchors.fill: parent
        Rectangle {
            id: header
            height: 10
            width: parent.width
            color: "#666"
            RowLayout {
                Text { text: titleText; color: "#fff" }
            }
        }
    }
}

2
实际上,你根本不需要一个占位符项目,你可以直接使用default property alias innerObject: innerColumn.children - dtech
关于在“本地”使用默认属性时未启用的问题,这似乎是一个错误。但实际上这并不重要。 - dtech

4
我将提供基于默认属性和重新设置父项的解决方案:
能够嵌入其他项的项: MyItem.qml
import QtQuick 2.7
import QtQuick.Layouts 1.2

Rectangle {
    id: root
    default property Item contentItem: null
    border {
        width: 1
        color: "#999"
    }
    ColumnLayout {
        anchors.fill: parent
        Rectangle {
            Layout.fillWidth: true
            height: 30
            color: "lightgreen"
        }
        Item {
            id: container
            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }
    onContentItemChanged: {
        if(root.contentItem !== null)
            root.contentItem.parent = container;
    }
}

可以按以下方式使用:
import QtQuick 2.7
import QtQuick.Window 2.0

Window {
    visible: true
    width: 600
    height: 600

    MyItem{
        width: 400
        height: 400
        anchors.centerIn: parent
        Text {
            text: "Hello!"
            anchors.centerIn: parent
        }
    }
}

但我仍然同意 @ddriver 的观点,Loader 是这种情况下最好的解决方案。


2

使用组件时不一定必须使用Loader,你可以这样做:

Loader {
   source: "Something.qml"
}

当源是同步加载的内容时,您可以直接使用加载器的item进行绑定等操作,无需担心它是否已创建。如果通过网络加载,则必须延迟绑定,直到完成项目,并分别使用声明式或命令式方式中的Binding元素或Qt.binding()
在您的情况下,加载器是适当的选择,内部动态对象的属性应为Component。这样,您可以使用内联组件或从现有源使用Qt.createComponent()来填充它。
property Component innerObject
...
innerObject: Component { stuff }
...
innerObject: Qt.CreateComponent(source)

当然,还有更高级的方法可以实现它,例如我在这里所概述的“通用QML模型对象”。它允许以声明式和命令式的方式快速轻松地创建任意数据结构树,并且由于该对象也是一个模型,因此您可以直接使用列表视图或重复器位置元素来布局GUI,而无需每次编写UI代码。
另外,从您的main.qml代码示例中可以看出,在qml文件中不能有多个根元素。 编辑:如果将元素移动到自己的qml文件中,则默认属性方法实际上也可以正常工作,因此您可以简单地:
default property alias innerObject: innerColumn.children

其中innerColumn是您的ColumnLayout的ID。此外,innerObject可以是任何合法名称,因为作为默认属性,它实际上不会被使用。

还有一种选项是不使用默认属性,这在根项目仍需要拥有自己的子项,但仍具有将声明对象重定向为子对象的能力时非常有用:

property alias content: innerColumn.children
// and then
content: [ Obj1{}, Obj2{}, Obj3{} ] // will become children of innerColumn

Card { titleText: "图像查看器" data: Rectangle { Component.onCompleted: { console.log(parent.objectName) } } } 如果在 Card.qml 中定义了默认属性别名 data : inner_space.data,其中 inner_space 是 Item { id: inner_space; objectName: "inner" },则会打印内部对象名称。 - Teemu Risikko
@TeemuRisikko - 如果将它移动到单独的qml文件中,它实际上是可以工作的。我从答案中删除了那部分内容。 - dtech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接