Solved ListView goes up when model updated
-
Looks like you are updating the currentIndex of the view as well. Because of this it may be moving. Please check. Other wise give sample to check this.
-
@DavidM29 that's normal.
updating the whole model, instead of the individual items will trigger a complete recreation of the ListView Content -> currentIndex is reset to 0
You can store the currentIndex in a property and set it on model changed
ListView { property int savedIndex = 0; onCurrentIndexChanged: savedIndex = currentIndex //eventually check against != 0 first onModelChanged: currentIndex = savedIndex }
-
@J.Hilk
After a model change the previoussavedIndex
might be higher than the new number of rows, I don't know whether you have to check for that to avoid an error? Whether it's always sensible to restore the current index if the model change is completely different rows is another question? A different possibility is to save the current item's value/text and then search to restore to that only if present. -
@JonB
QML is not c++ ;-)
If you set a current index that is outside the range of the list, it defaults to 0The only thing you have to consider/check, if the currentIndex changed signal is emitted before the modelChanged signal. Because you may end up overwriting savedIndex with 0
-
@J.Hilk
Thank you for your suggestion I'm going to try that. It seems to be a good solution. -
@J-Hilk @JonB @dheerendra
It does not work on my case obviously. I'll have to find another solution.
I use a ScrollView and inside this scrollview I do have my ListView. And I believe that when the ListView model is update it also refresh the Scrollview. So whatever index I do have in my ListView it goes back to the top.Edit :
Here is some sample code of the part I'm working with you should be able to run it :
main.qml :
import QtQuick 2.0 import QtQuick.Window 2.2 import QtQuick.Controls 1.3 import QtQuick.Controls.Styles 1.3 Window { id: window visible: true width: 640 height: 480 title: qsTr("Hello World") ScrollView{ id :view anchors.fill: parent flickableItem.flickableDirection: Flickable.VerticalFlick flickableItem.boundsBehavior: Flickable.StopAtBounds flickableItem.interactive: true style: ScrollViewStyle { handle: Rectangle { implicitWidth: 10 implicitHeight: 30 radius: 5 color: "grey" } scrollBarBackground: Rectangle { implicitWidth: 15 color: "transparent" } decrementControl: Rectangle { implicitWidth: 0 implicitHeight: 0 } incrementControl: Rectangle { implicitWidth: 0 implicitHeight: 0 } } ListView { id: dataView property int savedIndex : 0 clip: true spacing : 2 model: window.model delegate: MyDelegate{} interactive: false onCurrentIndexChanged:{ console.log("Current index changed") if(currentIndex !==0){ savedIndex = currentIndex } } onModelChanged: currentIndex = savedIndex } } property var model: [ {"type": "switch", "optionName": qsTr("Option 1"), "value" : true }, {"type": "switch", "optionName": qsTr("Option 2"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 3"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 4"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 5"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 6"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 7"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 8"), "value" :false }, {"type": "switch", "optionName": qsTr("Option 9"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 10"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 11"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 12"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 13"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 14"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 15"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 16"), "value" : true}, {"type": "switch", "optionName": qsTr("Option 17"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 18"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 19"), "value" : false}, {"type": "switch", "optionName": qsTr("Option 20"), "value" : true}, ] function updateModel(){ dataView.model = window.model } }
MyDelegate.qml
import QtQuick 2.0 Item { id: multiDelegate height: multiDelegate.ListView.view.height/8 width: multiDelegate.ListView.view.width property var myItem: model.modelData ? model.modelData : model property int selectLanguage: 1 function bestDelegate(t) { switch(t){ case "switch": return switchDelegate; case "input" : return inputDelegate; default : return console.log("Type not found : " + t) } } Component { id: switchDelegate SwitchOption { id: night1 height: parent.height width: parent.width nomOption: myItem.optionName bouton.checked: myItem.value bouton.onCheckedChanged: { window.model[index].value = bouton.checked updateModel() } } } Component { id : inputDelegate TextInputOption{ id: reglageUsine height: parent.height / 8 width: parent.width nomOption: qsTrId(myItem.optionName) unite: myItem.unite text: myItem.value onTextChanged: { window.model[index].value = text } } } Loader { id: itemDisplay anchors.fill: parent; anchors.topMargin: 2 anchors.bottomMargin: 2 sourceComponent: bestDelegate(myItem.type) } }
SwitchOption.qml :
import QtQuick 2.4 import QtQuick.Controls 1.3 import QtQuick.Layouts 1.1 Item { property string nomOption : "Option" property alias bouton : bouton RowLayout{ width: parent.width height: parent.height spacing: 0 Rectangle{ Layout.preferredWidth: parent.width*0.54 Layout.preferredHeight: parent.height anchors.right: value.left color: "transparent" Rectangle{ anchors.fill: parent anchors.leftMargin: parent.width/10 color: "transparent" Text { id: textOption text: nomOption font.pixelSize: 15 color: "black" anchors.verticalCenter: parent.verticalCenter } } } Rectangle{ id:value Layout.preferredWidth: parent.width*0.45 Layout.preferredHeight: parent.height Layout.alignment: Qt.AlignRight color: "transparent" CheckBox { id: bouton height: parent.height*4/7 width: height*2 anchors.horizontalCenter: parent.horizontalCenter checked:false anchors.verticalCenter: parent.verticalCenter } } } }
TextInputOption.qml :
import QtQuick 2.4 import QtQuick.Controls 1.3 import QtQuick.Controls.Styles 1.3 import QtQuick.Layouts 1.1 Item { //Le libellé de l'option property string nomOption : "Option" property string unite: "" property string text: "" RowLayout{ width: parent.width height: parent.height spacing: 0 Rectangle{ Layout.preferredWidth: parent.width*0.54 Layout.preferredHeight: parent.height anchors.right: value.left color: "transparent" Rectangle{ anchors.fill: parent anchors.leftMargin: parent.width/10 color: "transparent" Text { id: textOption text: nomOption color: "black" font.pixelSize: 15 anchors.verticalCenter: parent.verticalCenter } } } Rectangle{ id: value Layout.preferredWidth: parent.width*0.45 Layout.preferredHeight: parent.height Layout.alignment: Qt.AlignRight color: "transparent" CustomTextField{ id:valeur isNumeric: true width: parent.width*0.55 height: parent.height*0.8 anchors.centerIn: parent input.text: text input.onTextChanged: { text = input.text } } Text{ anchors.left : valeur.right anchors.leftMargin: 5 anchors.verticalCenter: valeur.verticalCenter font.family: localFont.name color: "black" font.pixelSize: 15 text : unite } } } }
CustomTextField.qml :
import QtQuick 2.0 Item { width: 150 height: 35 property bool isNumeric: false property string regex: "" property bool isMDP: false property alias input:textFieldIn Rectangle{ anchors.fill: parent border.width: 1 border.color:textFieldIn.focus ? "red" : "grey" radius: height/2 color: "darkgrey" TextInput{ id: textFieldIn anchors.fill: parent layer.enabled: true font.pixelSize: 15 verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignHCenter cursorVisible: false echoMode: isMDP ? TextInput.Password : TextInput.Normal onFocusChanged: { if (focus) keyboard.setTarget(this, isNumeric, regex, isMDP) } } } }
When I do run this code my ScrollView goes all the way up each time I change a value on my list.
It is because I update the model. But if I don't update the model I loose the change when the value is out of the view (is not rendered anymore and once it comes back it reload the initial model which is not the right value anymore) -
-
@fcarney
I don't need to center my view onComplete. I want to keep the position of my ScrollView when updating the model on the ListView.
But if you do have any other thought do not hesitate. -
@DavidM29
I was a tiny bit of, you have to use contentY and not currentIndex. Simply scrolling through a list view apparently does not change the currentIndex at all....this works:
import QtQuick 2.12 import QtQuick.Controls 2.5 ApplicationWindow { id:root visible:true width:500; height:500 property bool switchState: false property bool justChanged: false Timer{ running: true interval: 2000 repeat: true onTriggered:{ justChanged = true; switchState = !switchState } } ListView{ id:lView anchors.fill: parent model: root.switchState ? 20 : 22 property int savedContentY: 0 Component.onCompleted: savedContentY = contentY onModelChanged:{ contentY = savedContentY } onContentYChanged:{ if(!justChanged) savedContentY = contentY; justChanged = false; } delegate: Text { text: modelData height: 50 color: index % 2 ? "red" : "blue" } } }
-
@J.Hilk
I tried to adapt your solution to my problem. But it does not work.
I'm not sure that the use of the Scrollview is compatible with your solution.I don't know what cause the Scrollview to go all the way up when my listview model is updated.
I'm still not able to prevent it from happening.Here is the code I tried (but keep in mind that my listview is inside a Scrollview and I can't change this):
ScrollView{ id :view anchors.fill: parent property int savedY : 0 flickableItem.flickableDirection: Flickable.VerticalFlick flickableItem.boundsBehavior: Flickable.StopAtBounds flickableItem.interactive: true style: ScrollViewStyle { handle: Rectangle { implicitWidth: 10 implicitHeight: 30 radius: 5 color: "grey" } scrollBarBackground: Rectangle { implicitWidth: 15 color: "transparent" } decrementControl: Rectangle { implicitWidth: 0 implicitHeight: 0 } incrementControl: Rectangle { implicitWidth: 0 implicitHeight: 0 } } onYChanged: { y = savedY; } ListView { id: dataView clip: true spacing : 2 model: window.model delegate: MyDelegate{} interactive: false onModelChanged:{ view.savedY = view.y; } } }
-
hi @DavidM29
import QtQuick 2.12 import QtQuick.Controls 1.5 ApplicationWindow { id:root visible:true width:500; height:500 Timer{ running: true interval: 2000 repeat: true onTriggered: view.changingModel++ } ScrollView{ id :view anchors.fill: parent property int savedY : 0 flickableItem.flickableDirection: Flickable.VerticalFlick flickableItem.boundsBehavior: Flickable.StopAtBounds flickableItem.interactive: true flickableItem.onContentYChanged: { console.log(flickableItem.contentY) if(flickableItem.contentY > 0) savedY = flickableItem.contentY } property int changingModel: 5 ListView { id: dataView clip: true spacing : 2 model: view.changingModel delegate: Rectangle{color: "red"; width: dataView.width; height: dataView.height/4} interactive: false onModelChanged:{ view.flickableItem.contentY = view.savedY; } } } }
-
@J.Hilk
It works !
Thank you !
Did not know that I had to use the flickableItem.
Wish I could use some fresh Qt 5.12.