I am having a very specific issue when using a QML ListView element in combination with its section properties.
I am using Qt 4.8.6, but I also have the same issue when I try this in Qt 5.3.1.
The following code can also be run in older versions of Qt by simply changing the import statement to
import QtQuick 1.0 (For < Qt 4.7.4)
or
import QtQuick 1.1 (For >= Qt 4.7.4)
Here is a standalone use case to demonstrate my problem:
import QtQuick 2.2
Rectangle {
width: 800
height: 800
color: "black"
property int pageNumber: 1
property int totalPages: Math.ceil(animalListView.contentHeight/animalListView.height)
Text {
x: 2
y: 90
color: "Orange"
text: "Expected height: " + (animalListView.count*70 + (50*10))
font.pixelSize: 28
}
Text {
x: 2
y: 0
color: "Orange"
text: "Actual ContentHeight: " + animalListView.contentHeight
font.pixelSize: 28
}
Text {
x: 2
y: 30
color: "Orange"
text: "Actual ChildrenRectHeight: " + animalListView.childrenRect.height
font.pixelSize: 28
}
Text {
x: 2
y: 60
color: "Orange"
text: "Total model items (minus sections): " + animalListView.count
font.pixelSize: 28
}
Rectangle {
id: boundingRect
width: 640
height: 500
x: 20
y: 200
radius: 10
border.width: 1
border.color: "green"
color: "transparent"
// The delegate for each section header
Component {
id: sectionHeaderDelegate
Rectangle {
width: parent.width
height: 50 // this is the problem
color: "transparent"
Text {
anchors.left: parent.left
id: headerText
text: section
color: "red"
}
Rectangle {
anchors.fill: parent
border.color: "purple"
border.width: 1
color: "transparent"
}
}
}
ListModel {
id: animalsModel
ListElement { name: "1Parrot"; size: "Small" }
ListElement { name: "2Guinea pig"; size: "Small" }
ListElement { name: "3Dog"; size: "Medium" }
ListElement { name: "4Cat"; size: "Medium" }
ListElement { name: "5Elephant"; size: "Medium" }
ListElement { name: "6Parrot"; size: "Small" }
ListElement { name: "7Guinea pig"; size: "Small" }
ListElement { name: "8Dog"; size: "Medium" }
ListElement { name: "9Cat"; size: "Medium" }
ListElement { name: "10Elephant"; size: "Large" }
ListElement { name: "11Parrot"; size: "Large" }
ListElement { name: "12Guinea pig"; size: "Large" }
ListElement { name: "13Dog"; size: "Large" }
ListElement { name: "14Cat"; size: "Medium" }
ListElement { name: "15Elephant"; size: "Large" }
ListElement { name: "16Parrot"; size: "Small" }
ListElement { name: "17Guinea pig"; size: "Small" }
ListElement { name: "18Dog"; size: "Medium" }
ListElement { name: "19Cat"; size: "Medium" }
ListElement { name: "20Elephant"; size: "Large" }
}
ListView {
id: animalListView
anchors.fill: parent
anchors.margins: 10
clip: true
interactive: true
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
model: animalsModel
delegate: Item {
width: parent.width
height: 70
Text {
text: name
color: "green"
}
Rectangle {
anchors.fill: parent
border.color: "yellow"
border.width: 1
color: "transparent"
}
}
section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: sectionHeaderDelegate
}
}
Rectangle {
anchors.top: boundingRect.top
anchors.left: boundingRect.right
anchors.leftMargin: 20
width: 40
height: 40
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
if (pageNumber > 1) {
animalListView.contentY -= animalListView.height;
animalListView.returnToBounds();
--pageNumber;
}
}
}
enabled: (!animalListView.atYBeginning)
visible: !(animalListView.atYBeginning && animalListView.atYEnd)
Text {
anchors.centerIn: parent
font.family: "Wingdings 3"
font.pixelSize: 40
text: "Ç" // Up arrow
}
}
Text {
visible: totalPages > 1
anchors.left: boundingRect.right
anchors.verticalCenter: boundingRect.verticalCenter
width: 100
height: 20
font.pixelSize: 18
horizontalAlignment: Text.AlignHCenter
color: "red"
text: qsTr("%1 of %2").arg(pageNumber).arg(totalPages)
}
Rectangle {
anchors.bottom: boundingRect.bottom
anchors.left: boundingRect.right
anchors.leftMargin: 20
width: 40
height: 40
color: "orange"
MouseArea {
anchors.fill: parent
onClicked: {
if (pageNumber < totalPages) {
animalListView.contentY += animalListView.height;
++pageNumber;
}
}
}
enabled: (!animalListView.atYEnd)
visible: !(animalListView.atYBeginning && animalListView.atYEnd)
Text {
anchors.centerIn: parent
font.family: "Wingdings 3"
font.pixelSize: 40
text: "È" // Down arrow
}
}
}
I am using the ListView to display a list of animal models, categorized by their size. In order to achieve this categorization in the view, I use the section.property, section.critiria and section.delegate properties of the ListView as implemented in the code given above.
(Note: Please ignore the fact that the model I supply to the ListView is not sorted, I understand that this will create numerous duplicate category entries in the ListView. This is beside the point here.)
When the number of models exceed the visible area of the ListView, I am using the property totalPages to calculate how many full ListView pages there are for navigation. The Up arrow and Down arrow buttons simply decrement and increment the content.Y of the ListView by the height of the ListView respectively.
The problem is that the contentHeight of the ListView does not remain static, it is dynamically changing and causing my totalPages property calculation to be incorrect.
It is interesting to note that this behavior occurs if and only if I set a height for my sectionHeaderDelegate rectangle. If I comment out the height statement (height: 50), the contentHeight of the ListView remains static, as expected - with the downside that the section headers/categories are now on top of the model text, which is not useful at all.
So my question is, why does the contentHeight of the QML ListView element dynamically change if and only if I use a section delegate who's height has been set to a non-zero value?
Also, I have left the following properties in the ListView for testing purposes, the ListView should be used with the Up/Down arrows:
interactive: true flickableDirection: Flickable.VerticalFlick boundsBehavior: Flickable.StopAtBounds
A ListView displays data from models created from built-in QML types like ListModel. A ListView has a model, which defines the data to be displayed, and a delegate, which defines how the data should be displayed. Items in a ListView are laid out horizontally or vertically.
The Flickable item places its children on a surface that can be dragged and flicked, causing the view onto the child items to scroll. This behavior forms the basis of Items that are designed to show large numbers of child items, such as ListView and GridView.
Similarly, encapsulating an instance of the data in a delegate allows the developer to dictate how to present or handle the data. Model - contains the data and its structure. There are several QML types for creating models. View - a container that displays the data. The view might display the data in a list or a grid.
Loader is used to dynamically load QML components. Loader can load a QML file (using the source property) or a Component object (using the sourceComponent property).
I know this is ancient, but I'll answer it here anyway because I was looking for a solution;
If you have a fixed height for your items, you can set the height of the container dynamically by simply setting the value by formula:
MyContainerWithListItems {
height: MyModel.items.length * height
}
If you have variable height items it will be more difficult; the solution is probably to have an onChange event fire off a function which crawls through your items and manually adds up the heights.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With