如何在QML TreeView中添加复选框并实现父子节点选中联动?
QML TreeView 复选框父子联动实现方案
要实现父节点勾选时自动联动所有子节点、子节点状态变化同步更新父节点状态的效果,按以下步骤修改即可:
前置准备
首先给你的树模型sysConfig的每个节点新增布尔类型的isChecked角色,用来存储节点选中状态。如果需要支持半选状态,该字段允许存null值代表半选。
核心逻辑
联动逻辑分两部分:
- 父节点点击选中/取消选中时,递归遍历当前节点下所有层级的子节点,将所有子节点的选中状态和父节点保持一致
- 任意子节点选中状态变化时,向上逐级遍历父节点,统计每个父节点下所有直接子节点的选中情况:子节点全选则父节点设为选中,子节点全不选则父节点设为未选中,子节点部分选中则父节点设为半选
完整实现代码
TreeView { id: treeView anchors.fill: parent frameVisible: false anchors.top: mainToolBar.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.leftMargin: 1 model: sysConfig // 递归设置当前节点及所有子节点选中状态 function setChildCheckState(parentIndex, checkState) { const childCount = model.rowCount(parentIndex) // 更新当前节点状态 model.setData(parentIndex, checkState, Qt.UserRole + 1) // 遍历处理所有子节点 for(let i = 0; i < childCount; i++) { const childIndex = model.index(i, 0, parentIndex) setChildCheckState(childIndex, checkState) } // 子节点更新完成后同步父节点状态 updateParentCheckState(parentIndex) } // 向上递归更新所有父节点的选中/半选状态 function updateParentCheckState(childIndex) { const parentIndex = childIndex.parent // 到达根节点,终止递归 if(!parentIndex.isValid) return const childCount = model.rowCount(parentIndex) let checkedNum = 0 let uncheckedNum = 0 // 统计当前父节点下所有直接子节点的选中情况 for(let i = 0; i < childCount; i++) { const currentIdx = model.index(i, 0, parentIndex) const currentState = model.data(currentIdx, Qt.UserRole + 1) if(currentState === true) checkedNum++ else if(currentState === false) uncheckedNum++ else { // 存在半选子节点,当前父节点直接判定为半选 checkedNum = 1 uncheckedNum = 1 break } } let parentState if(checkedNum === childCount) parentState = true else if(uncheckedNum === childCount) parentState = false else parentState = null model.setData(parentIndex, parentState, Qt.UserRole + 1) // 继续向上更新更高层级父节点 updateParentCheckState(parentIndex) } itemDelegate: CheckBox { id: checkbox width: parent.width indicator.height: 20 indicator.width: 20 text: styleData.value // 绑定模型存储的状态 checked: model.data(styleData.index, Qt.UserRole + 1) === true partiallyChecked: model.data(styleData.index, Qt.UserRole + 1) === null onClicked: { // 仅用户手动点击时触发联动,避免代码修改状态导致循环触发 treeView.setChildCheckState(styleData.index, checked) } } }
注意事项
- 代码中
Qt.UserRole + 1是自定义C++模型中isChecked角色对应的枚举值,如果你自己实现的QAbstractItemModel子类中给isChecked分配的角色值不是这个,替换成对应值即可。如果是用QML原生ListModel配合TreeView使用,直接通过model.isChecked访问即可,不需要写角色偏移值。 - 联动逻辑必须放在CheckBox的
onClicked信号中处理,不要写在onCheckedChanged里,后者会在代码修改checked状态时也触发,极易引发递归死循环。 - 半选状态不需要单独持久化存储,每次子节点状态变化时动态计算父节点状态即可,减少状态维护成本。
内容的提问来源于stack exchange,提问作者aidibos
相关产品推荐
相关产品推荐

