Как определить "шаблон" с дочерними заполнителями в QML?
Мне очень нравится 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 в заполнитель, а родительские привязки должны быть такими же, что и в элементе-заполнителе, а не в элементе шаблона.
Ответы
Ответ 1
Я нашел гораздо более хороший ответ на этот вопрос, предложенный в презентации Qt Developer Days 2011 "Краткие рекомендации и шаблоны Qt" .
Они используют default property 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
}
}
}
Ответ 2
Некро отвечает на случай, если кто-то еще окажется здесь, как я.
В Qt5 где-то вдоль строки свойство по умолчанию стало "data", а не "children".
Это позволяет добавлять объекты других типов, кроме "Item".
например Соединения также могут быть добавлены (чтобы ответить на мой вопрос выше)
Так что в 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
...
}
Ответ 3
Собственно, правильный ответ из того, что я слышал, заключается в использовании QML Loader, чтобы выполнить то, что вы хотите.
[это сказано; Я еще не пробовал это, но это в моем ближайшем списке задач и выглядит довольно прямолинейно]
Кроме того, поиск stackoverflow для другого "QML Loader" задает вопросы, так как есть число, которое поможет вам начать работу.
Ответ 4
Вы можете переместить элементы (если вы хотите поддерживать несколько элементов в заполнителе) с помощью этого фрагмента кода:
property list<Item> contents
Component.onCompleted: {
var contentItems = [];
for(var i = 0; i < contents.length; ++i)
contentItems.push(contents[i]);
placeholder.children = contentItems;
}
Обратите внимание, что вам не нужно предоставлять список элементов для свойства содержимого, так как одиночные значения будут приниматься движком QML также для свойств списка.
Ответ 5
Короче (чтобы показать идею):
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()
}
}
Надеюсь, что это поможет:)