Views
6 months ago

tornadofx-guide

5. Data Controls Next we

5. Data Controls Next we specify the highest root for the TreeView that all departments will be nested under, and we give it a placeholder Person called "Departments". cellFormat { text = it.name } Then we specify the cellFormat() to render the name of each Person (including departments) on each cell. populate { parent -> if (parent == root) departments else persons.filter { it.department == parent. value.name } } Finally, we call the populate() function and provide a block instructing how to provide children to each parent . If the parent is indeed the root , then we return the departments . Otherwise the parent is a department and we provide a list of Person objects belonging to that department . Data driven TreeView If the child list you return from populate is an ObservableList , any changes to that list will automatically be reflected in the TreeView. The populate function will be called for any new children that appears, and removed items will result in removed TreeItems as well. TreeView with Differing Types It is not necessarily intuitive to make every entity in the previous example a Person . We made each department a Person as well as the root "Departments". For a more complex TreeView where T is unknown and can be any number of types, it is better to specify type T as Any . Using star projection, you can safely populate multiple types nested into the TreeView . For instance, you can create a Department type and leverage cellFormat() to utilize typechecking for rendering. Then you can use a populate() function that will iterate over each element, and you specify the children for each element (if any). 64

5. Data Controls data class Department(val name: String) // Create Department objects for the departments by getting distinct values from Perso n.department val departments = persons.map { it.department }.distinct().map { Department(it) } // Type safe way of extracting the correct TreeItem text cellFormat { text = when (it) { is String -> it is Department -> it.name is Person -> it.name else -> throw IllegalArgumentException("Invalid value type") } } // Generate items. Children of the root item will contain departments, children of dep artments are filtered populate { parent -> val value = parent.value if (parent == root) departments else if (value is Department) persons.filter { it.department == value.name } else null } TreeTableView The TreeTableView operates and functions similarly to a TreeView , but it has multiple columns since it is a table. Please note that the columns in a TreeTableView are the same for each parent and child element. If you want the columns to be different between parent and child, use a TableView with a rowExpander() as covered earlier in this chapter. Say you have a Person class that optionally has an employees parameter, which defaults to an empty List if nobody reports to that Person . class Person(val name: String, val department: String, val email: String, val employees: List = emptyList()) Then you have an ObservableList holding instances of this class. 65

Guide
Guide
GUIDE
GUIDE
GUIDE
GUIDE
GUIDE
Guide
GUIDE
Guide
GUIDE
Guide
GUIDE
Guide
GUIDE
Guide
Guide
GUIDE
GUIDE
GUIDE
GUIDE
guide
Guide
GUIDE
GUIDE
Guide
Guide to
The Guide
SELECTION GUIDE SELECTION GUIDE
The Ultimate Guide The Ultimate Guide