30

我真的很喜欢 QML。我喜欢如何定义组件(类似于类)及其属性,并从其他地方实例化它们(类似于对象)。

我可以定义,比如说,一个按钮,有一些外观和感觉,以及一个标签文本。例如,可以使用此组件定义 ( Button.qml) 来完成此操作:

Item {
    id: button
    property string label

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Text {
            anchors.centerIn: parent
            font.pixelSize: 20
            text: button.label
            color: "white"
        }
    }
}

并在此主文件 ( ) 中实例化main.qml

Rectangle {
    width: 300
    height: 200

    Button {
        anchors.centerIn: parent
        anchors.margins: 50
        label: "Hello button!"
    }
}

但是我看到了以下限制:我只能定义一个带有一些属性的按钮模板,而不是一些占位符。实例中定义的所有孩子都将是直接孩子,至少在默认情况下,我想改变这种行为。

假设我想在按钮中放置一个项目(假设是一个图像,但我不想告诉Button它将是一个图像的定义)。我想象这样的事情:

Item {
    id: button
    property Item contents   <-- the client can set the placeholder content here

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }

    Component.onCompleted: {
        // move the contents into the placeholder...
    }
}

我怎样才能做到这一点?我不知道使用Component.onCompleted是否正确。但是请注意,在我的情况下,内容之后永远不会改变(至少在我当前的应用程序设计中......)。

另外,我希望锚定在占位符内工作。例如,如果我将内容定义为 Text 元素,以它的父元素为中心(首先是模板本身)。然后我的代码将此 Text 实例移动到占位符中,然后父锚点应该是占位符项的那些,而不是模板项。

4

5 回答 5

41

我在 Qt Developer Days 2011 "Qt Quick Best Practices and Design Patterns"的演示文稿中找到了这个问题的更好答案。

它们用于default property alias ...将子项别名为任何项的任何属性。如果您不想给孩子起别名但给 alias 属性命名,只需删除default. (文字子项是每个 QML 定义的默认属性的值。)

Item {
    id: button
    default property alias contents: placeholder.children

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }
}
于 2012-10-07T20:43:51.693 回答
13

死灵回答,以防其他人像我一样在这里结束。

在 Qt5 中的某个地方,默认属性变成了“数据”而不是“孩子”。这使得添加“项目”以外的其他对象类型成为可能。例如,也可以添加连接(回答我自己的问题)

所以在Qt5中你应该这样做:

Item {
    id: button
    default property alias contents: placeholder.data

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }
}

请注意: placeholder.data而不是placeholder.children 另请注意,您不必使用别名contents- 这可以是您喜欢的任何名称。一个例子:

Item {
    id: button
    default property alias foo: placeholder.data
    ...

}
于 2016-01-06T15:59:22.540 回答
3

实际上,我所听到的正确答案是使用QML Loader来完成您想要的。

[话虽如此; 我还没有真正尝试过,但它在我的近期尝试清单上,看起来相当简单]

此外,在 stackoverflow 中搜索其他“QML Loader”问题,因为有一个数字可以帮助您入门。

于 2012-09-18T13:32:50.237 回答
1

简而言之(展示这个想法):

import QtQuick 1.1

Item {
    width: 200
    height: 100

    //<-- the client can set the placeholder content here
    property Item contents: Rectangle {
        anchors.fill: parent
        anchors.margins: 25
        color: "red"
    }

    Rectangle {
        id: container

        anchors.fill: parent
        radius: 10
        color: "gray"

        //<-- where the placeholder should be inserted
    }

    Component.onCompleted: {
        contents.parent = container
    }
}

稍长的版本(支持内容重新分配):

import QtQuick 1.1

Item {
    width: 200
    height: 100

    //<-- the client can set the placeholder content here
    property Item contents: Rectangle {
        //anchors can be "presupplied", or set within the insertion code
        //anchors.fill: parent
        //anchors.margins: 25
        color: "red"
    }

    Rectangle {
        id: container

        anchors.fill: parent
        radius: 10
        color: "gray"

        //<-- where the placeholder should be inserted
        //Item {
        //    id: placeholder
        //}
    }

    //"__" means private by QML convention
    function __insertContents() {
        // move the contents into the placeholder...
        contents.parent = container
        contents.anchors.fill = container
        contents.anchors.margins = 25
    }

    onContentsChanged: {
        if (contents !== null)
            __insertContents()
    }

    Component.onCompleted: {
        __insertContents()
    }
}

希望这可以帮助 :)

于 2012-10-07T20:10:35.767 回答
1

您可以使用以下代码移动项目(如果您想在占位符中支持多个项目):

property list<Item> contents

Component.onCompleted: {
    var contentItems = [];
    for(var i = 0; i < contents.length; ++i)
        contentItems.push(contents[i]);
    placeholder.children = contentItems;
}

请注意,您不必为内容属性提供项目列表,因为 QML 引擎也将接受单个值用于列表属性。

于 2012-09-18T13:33:24.540 回答