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

时间:2017-01-04 14:38:11

标签: kotlin tornadofx

我有以下组件:

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,并且可以访问每个孩子的模型。

我尝试用帮助&#34; openInternalWindow&#34; ,但我无法创建多个子实例,我必须手动管理列表 - 这很糟糕。

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方式?

1 个答案:

答案 0 :(得分:3)

在这个例子中,我将使用视图模型和范围来跟踪每个乐器编辑器的项目。我们需要确保这些工具是唯一的,因此我们可以在编辑器关闭时将它们从列表中删除。我创建了一个带有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()
    }
}

我们想要一个可以在仪器编辑器中注入的视图模型。我们将确保默认情况下视图模型包含新工具。它包含name属性的外观,因此我们可以将它绑定到编辑器输入字段。

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

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

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

如果要从操作中关闭编辑器,可以从片段内的任何位置调用closeModal()

我已经包含了一个带有TableView的完整示例应用程序,该应用程序显示了当前打开的乐器。它将如下图所示。请注意,在从模型中刷新更改并在表中可见之前,需要单击“保存”。 sample app

我希望这是您正在寻找的,或者您至少可以根据此示例修改它以适合您的用例。

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

}