TornadoFX ¿Cómo crear MDI con una list de models de windows hijas?

Tengo los siguientes componentes:

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() ... } 

Resumen: quiero crear MDI y tener acceso al model para cada niño.

Intento hacer esto con la ayuda "openInternalWindow", pero no puedo crear varias instancias secundarias y debo administrar la list manualmente, es malo.

 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) } } } } } } 

¿Cómo hacerlo bien?

En este ejemplo, usaré un model de vista y un scope para hacer un seguimiento del artículo para cada editor de instrumentos. Necesitamos asegurarnos de que los instrumentos sean únicos para que podamos eliminarlos de la list cuando se cierre el editor. Creé un object de dominio Instrument con un id y un nombre:

 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() } } 

Queremos un model de vista que podamos inyectar en el editor de instrumentos. Nos aseguraremos de que el model de vista contenga un nuevo instrumento por defecto. Contiene una fachada para la propiedad de nombre para que podamos vincularlo a un campo de input del editor.

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

Un Fragment tiene devoluciones de llamada para onDock y onUndock que se pueden utilizar para realizar un seguimiento del model de ese fragment. Podemos usar events para señalar esto. Declara los siguientes events:

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

Anule las devoluciones de llamada de acoplamiento en InstrumentFragment para activar estos events:

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

Por ahora, mantendremos la list de instrumentos en la vista principal, InstrumentsView . Esto también podría ser en un Controller .

 val instruments = FXCollections.observableArrayList<Instrument>() 

En la class init de la vista principal, nos suscribiremos a los events que creamos y modificamos nuestra list:

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

La acción "Nuevo Instrumento" abrirá un nuevo InstrumentEditor en un nuevo Scope para que podamos inyectar el model de vista en él y get una instancia única para ese editor.

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

Desafortunadamente, no podemos usar openInternalWindow ya que actualmente solo admite una window interna a la vez. Por lo tanto, usé openWindow en openWindow lugar.

Si desea cerrar el editor de una acción, puede llamar a closeModal() desde cualquier lugar dentro del fragment.

He incluido una aplicación de ejemplo completa con un TableView que muestra los instrumentos actualmente abiertos. Se verá como la image de abajo. Observe que debe presionar Guardar antes de que los cambios se borren del model y sean visibles en la tabla. aplicación de muestra

Espero que esto sea lo que está buscando, o que al less pueda modificarlo para que se ajuste a su caso de uso basado en esta muestra.

 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) } } } 
  • Tornadofx - controlando el object de la fila de la vista de tabla mientras construye
  • Desplazamiento suave en JavaFX TableView
  • El elemento TornadoFx ItemViewModel es nulo
  • TornadoFX filechooser
  • TornadoFX envuelve las classs de dominio en ItemViewModel
  • Cómo mostrar datos usando la vista de tree de TornadoFX
  • Las properties vinculadas no funcionan
  • Cómo cambiar la vista en TornadoFx
  • Animación consecutiva en TornadoFX?
  • ¿Cuál es la mejor práctica para crear un componente de IU personalizado en tornadofx?
  • Tornadofx tableview sincroniza dos tablas