TornadoFX如何用子窗口模型列表创建MDI?

我有以下组件:

class ChildModel:ViewModel() { //or it may be an POJO, it does not matter val value .... } class ParentView: View() { ... //Maybe this should be implemented into ParentViewModel val childrenList:List<ChildModel> fun addFragmentAsChild() { //should: // 1. display fragment within ParentView // 2. add fragment into modelList (or fragmentList - it does not matter - important to have access to the model of every child ) } fun deleteFragmentAsChild() { //should destroy child and remove item from childrenList //should work also on manual closing } } class ChildFragment: Fragment() { val model = ChildModel() ... } 

总结:我想创建MDI,并有权访问每个孩子的模型。

我尝试做这个帮助“openInternalWindow”,但我不能创建多个子实例,我必须手动管理列表 – 这是不好的。

 class InstrumentsView: View() { override val root = BorderPane() val instrumentList = ArrayList<InstrumentFragment>() init { with(root){ top = menubar { menu("Tools") { menuitem("Add instrument", "Shortcut+A") { val newFragment = InstrumentFragment() instrumentList.add(newFragment) println(instrumentList.size) openInternalWindow(newFragment, modal = false) } } } } } } 

如何正确使用tornadofx的方式?

在这个例子中,我将使用视图模型和范围确定来跟踪每个乐器编辑器的项目。 我们需要确保这些仪器是独一无二的,所以我们可以在编辑器关闭时从列表中删除它们。 我创建了一个具有ID和名称的Instrument域对象:

 class Instrument { val idProperty = SimpleObjectProperty<UUID>(UUID.randomUUID()) var id by idProperty val nameProperty = SimpleStringProperty() var name by nameProperty override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false other as Instrument if (id != other.id) return false return true } override fun hashCode(): Int { return id.hashCode() } } 

我们需要一个我们可以在仪器编辑器中注入的视图模型。 我们将确保视图模型默认包含一个新的仪器。 它包含名称属性的外观,因此我们可以将其绑定到编辑器输入字段。

 class InstrumentModel: ItemViewModel<Instrument>() { init { item = Instrument() item.name = "New instrument" } val name = bind { item?.nameProperty } } 

FragmentonDockonUndock回调onUndock ,可以用来跟踪该片段的模型。 我们可以使用事件来表示这一点。 声明以下事件:

 class InstrumentAdded(val instrument: Instrument) : FXEvent() class InstrumentRemoved(val instrument: Instrument) : FXEvent() 

覆盖InstrumentFragment的对接回调以触发这些事件:

 override fun onDock() { fire(InstrumentAdded(model.item)) } override fun onUndock() { fire(InstrumentRemoved(model.item)) } 

现在我们将在主视图InstrumentsView保留乐器列表。 这也可以在Controller

 val instruments = FXCollections.observableArrayList<Instrument>() 

在主视图的init类中,我们将订阅我们创建的事件并修改我们的列表:

 subscribe<InstrumentAdded> { instruments.add(it.instrument) } subscribe<InstrumentRemoved> { instruments.remove(it.instrument) } 

“新仪器”操作将在一个新的Scope打开一个新的InstrumentEditor,这样我们就可以将视图模型注入到它中,并获得该编辑器唯一的实例。

 menuitem("Add instrument", "Shortcut+A") { find<InstrumentFragment>(Scope()).openWindow() } 

不幸的是,我们不能使用openInternalWindow因为它一次只支持一个内部窗口。 所以我用openWindow来代替。

如果你想关闭一个动作的编辑器,你可以从fragment中的任何地方调用closeModal()

我已经包含了一个完整的示例应用程序和一个显示当前打开的工具的TableView。 它将如下图所示。 请注意,在从模型中刷新更改并在表格中可见之前,您需要点击保存。 示例应用程序

我希望这是你正在寻找的东西,或者你至少可以根据这个示例修改它以适应你的用例。

 import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.FXCollections import tornadofx.* import java.util.* class Instrument { val idProperty = SimpleObjectProperty<UUID>(UUID.randomUUID()) var id by idProperty val nameProperty = SimpleStringProperty() var name by nameProperty override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false other as Instrument if (id != other.id) return false return true } override fun hashCode(): Int { return id.hashCode() } } class InstrumentModel : ItemViewModel<Instrument>() { init { item = Instrument() item.name = "New instrument" } val name = bind { item?.nameProperty } } class InstrumentAdded(val instrument: Instrument) : FXEvent() class InstrumentRemoved(val instrument: Instrument) : FXEvent() class InstrumentFragment : Fragment("Instrument Editor") { val model: InstrumentModel by inject() override val root = form { prefWidth = 300.0 fieldset("Edit instrument") { field("Name") { textfield(model.name) } } button("Save") { setOnAction { model.commit() } } } override fun onDock() { fire(InstrumentAdded(model.item)) } override fun onUndock() { fire(InstrumentRemoved(model.item)) } } class InstrumentsView : View() { val instruments = FXCollections.observableArrayList<Instrument>() override val root = borderpane { setPrefSize(400.0, 300.0) top { menubar { menu("Tools") { menuitem("Add instrument", "Shortcut+A") { find<InstrumentFragment>(Scope()).openWindow() } } } } center { tableview(instruments) { column("Name", Instrument::nameProperty) columnResizePolicy = SmartResize.POLICY } } } init { subscribe<InstrumentAdded> { instruments.add(it.instrument) } subscribe<InstrumentRemoved> { instruments.remove(it.instrument) } } }