TornadoFX 如何使用 child windows 模型列表创建 MDI?
TornadoFX How to create MDI with list of child windows models?
我有以下组件:
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 并可以访问每个 child 的模型。
我尝试在 "openInternalWindow" 的帮助下执行此操作,但我无法创建多个 child 实例并且我必须手动管理列表 - 这很糟糕。
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 }
}
A Fragment
具有 onDock
和 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>()
在主视图的初始化 class 中,我们将订阅我们创建的事件并修改我们的列表:
subscribe<InstrumentAdded> {
instruments.add(it.instrument)
}
subscribe<InstrumentRemoved> {
instruments.remove(it.instrument)
}
"New Instrument" 操作将在新的 Scope
中打开一个新的 InstrumentEditor,因此我们可以将视图模型注入其中并获得该编辑器独有的实例。
menuitem("Add instrument", "Shortcut+A") {
find<InstrumentFragment>(Scope()).openWindow()
}
遗憾的是,我们无法使用 openInternalWindow
,因为它目前一次仅支持一个内部 window。因此我改用 openWindow
。
如果您想通过操作关闭编辑器,可以从片段内的任何位置调用 closeModal()
。
我已经包含了一个完整的示例应用程序,其中包含一个显示当前打开的工具的 TableView。它将如下图所示。请注意,在更改从模型中刷新并在 table 中可见之前,您需要点击保存。
我希望这就是您正在寻找的,或者您至少可以根据此示例修改它以适合您的用例。
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)
}
}
}
我有以下组件:
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 并可以访问每个 child 的模型。
我尝试在 "openInternalWindow" 的帮助下执行此操作,但我无法创建多个 child 实例并且我必须手动管理列表 - 这很糟糕。
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 }
}
A Fragment
具有 onDock
和 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>()
在主视图的初始化 class 中,我们将订阅我们创建的事件并修改我们的列表:
subscribe<InstrumentAdded> {
instruments.add(it.instrument)
}
subscribe<InstrumentRemoved> {
instruments.remove(it.instrument)
}
"New Instrument" 操作将在新的 Scope
中打开一个新的 InstrumentEditor,因此我们可以将视图模型注入其中并获得该编辑器独有的实例。
menuitem("Add instrument", "Shortcut+A") {
find<InstrumentFragment>(Scope()).openWindow()
}
遗憾的是,我们无法使用 openInternalWindow
,因为它目前一次仅支持一个内部 window。因此我改用 openWindow
。
如果您想通过操作关闭编辑器,可以从片段内的任何位置调用 closeModal()
。
我已经包含了一个完整的示例应用程序,其中包含一个显示当前打开的工具的 TableView。它将如下图所示。请注意,在更改从模型中刷新并在 table 中可见之前,您需要点击保存。
我希望这就是您正在寻找的,或者您至少可以根据此示例修改它以适合您的用例。
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)
}
}
}