30

I really like QML. I like how I can define components (comparable to classes) and their properties, and instantiate them from somewhere else (comparable to objects).

I can define, let's say, a button, having some look and feel, and a label text on it. This could be done, for example, using this component definition (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"
        }
    }
}

and instanciated in this main file (main.qml):

Rectangle {
    width: 300
    height: 200

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

But I see the following restriction: I can only define a button template with some properties, not with some placeholder. All children defined in the instance will be direct children, at least per default, and I want to change this behavior.

Let's say I want to place an item (let's say an image, but I don't want to tell the definition of Button that it will be an image) in the button. I imagine something like this:

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...
    }
}

How can I achieve this? I don't know if using Component.onCompleted is the correct way. Note that, however, that in my case the contents will never change afterwards (at least in my current design of the application...).

Also, I want anchoring to work within the placeholder. For example, if I define the contents to be a Text element, being centered in its parent (which will first be the template itself). Then my code moves this Text instance into the placeholder and the parent anchors should then be those of the placeholder item, not the template item.

4

5 に答える 5

41

Qt DeveloperDays2011 「QtQuickBestPractices and Design Patterns」のプレゼンテーションで提案された、この質問に対するより良い答えを見つけました。

それらは、子アイテムを任意のアイテムの任意のプロパティにエイリアスdefault property alias ...するために使用します。にエイリアスを付けたくないが、エイリアスプロパティに名前を付ける場合は、を削除します。(リテラルの子は、QML定義ごとにデフォルトプロパティの値です。)default

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

Necro answering in case someone else end up here as I did.

In Qt5 somewhere along the line the default property became "data" and not "children". This makes it possible to add other object types than "Item". e.g. Connections can be added as well (to answer my own question above)

So in Qt5 you should do:

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
        }
    }
}

Note the: placeholder.data instead of placeholder.children Also please note that you don't have to use the alias name contents - this can be anything you like. An example:

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

}
于 2016-01-06T15:59:22.540 に答える
3

Actually, the correct answer from what I've heard is to use a QML Loader to accomplish what you want.

[that being said; I haven't actually tried it yet but it's on my near-term to-try list and looks fairly straight forward]

Also, search stackoverflow for other "QML Loader" questions as there are a number that will help you get started.

于 2012-09-18T13:32:50.237 に答える
1

You can move the item(s) (if you want to support multiple items within the placeholder) using this piece of code:

property list<Item> contents

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

Note that you do not have to provide a list of Items for the contents property, as single values will be accepted by the QML engine also for list properties.

于 2012-09-18T13:33:24.540 に答える
1

In short (to show the idea):

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
    }
}

Somewhat longer version (supporting contents reassignment):

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()
    }
}

Hope this helps :)

于 2012-10-07T20:10:35.767 に答える