edvin / tornadofx

Lightweight JavaFX Framework for Kotlin
Apache License 2.0
3.68k stars 272 forks source link

Data Driven TreeView #1356

Open toughstumpryan opened 2 years ago

toughstumpryan commented 2 years ago

Hello TornadoFx community!

I was hoping to get some clarification on a point in the Guide w.r.t. a data drive TreeView. I'm having trouble getting this working, and am wondering what I might be missing.

Here is my ViewModel:

class ContactViewModel : ViewModel() {

    private val activeMapProperty = mapProperty(observableMapOf<String, ServiceAnnouncement>())
    private val activeProperty = listProperty(observableListOf<ServiceAnnouncement>())
    val active: ObservableList<ServiceAnnouncement> by activeProperty  // explicit type due to platform call

    init { 
        AddressBook.ContactAdded += ::contactAdded
        activeProperty.bind(nonNullObjectBinding(activeMapProperty) { observableListOf(values) } )
    }

    private fun contactAdded(sender: Any, contact: ServiceAnnouncement) {
        activeMapProperty.putIfAbsent(contact.uid, contact)
    }
}

And in my View class:

class ContactTree : View() { 
    private val contactViewModel: ContactViewModel by inject()

    override val root: Parent = treeview<Any> {
        root = TreeItem("Active")
        root.isExpanded = true

        populate { parent ->
            when (val v = parent.value) {
                "Active" -> contactViewModel.active
                else -> null
            }
        }

        onUserSelect { println("$it") }
    }
}

From the Guide section on 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.

As you can see, my ContactViewModel::active property is type ObservableList<ServiceAnnouncement>, which meets the "if the child list you return is an ObservableList" criteria. And I was able to verify through the IntelliJ debugger that when ContactViewModel::contactAdded is called that all of activeMapProperty, activeProperty, and active reflect the update, but nothing happens in my UI.

Any advice would be greatly appreciated!

toughstumpryan commented 2 years ago

Update: I found that by using activeProperty directly things work as I expected. I wonder if anyone could offer insight as to why?