Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

ListView goes up when model updated



  • Hello,

    The ListView is going all the way to the top when the model is updated. Is there anyway to avoid this behavior ?


  • Qt Champions 2017

    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.


  • Moderators

    @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 previous savedIndex 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.


  • Moderators

    @JonB
    QML is not c++ ;-)
    If you set a current index that is outside the range of the list, it defaults to 0

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


  • Moderators

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

  • Moderators

    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.


Log in to reply