2017-01-04 14 views
0

У меня есть следующие компоненты: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 way?

+0

Вы хотите получить доступ к фактическим фрагментам или просто хотите получить список моделей, которые они содержат, и этот список должен обновляться при открытии/закрытии редактора фрагментов? Должен ли каждый InstrumentFragment управлять собственным/отдельным инструментом? PS: Никогда не создавайте экземпляр фрагмента напрямую, используйте find, inject или 'this + = InstrumentFragment'. –

+0

Мне нужен доступ к списку моделей (ofcource, я также могу получить ссылку на модель, если у меня будет список фрагментов - мне самое главное - чистое и элегантное решение), и я хочу обновить этот список после открытия/закрытия редактор фрагментов. Каждый IntrumentFragment знает только о своей модели (или имеет ссылку на родителя - почему бы и нет?). –

ответ

3

В этом примере я буду использовать модель представления и область обзора для отслеживания элемента для каждого инструментария. Нам нужно убедиться, что инструменты уникальны, поэтому мы можем удалить их из списка, когда редактор закрыт. Я создал объект домена 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 } 
} 

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

В классе инициализации главного вида, мы будем подписаться на события, которые мы создали и изменить наш список:

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

Акция «Новый инструмент» откроет новый InstrumentEditor в new Scope, чтобы мы могли ввести в него модель представления и получить экземпляр, уникальный для этого редактора.

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

} 
+0

Спасибо, Эдвин! Это то, что я хочу! Я никогда не использовал функциональность «Scope» и «EventBus». Надо учиться. –

+0

Рад это услышать :) Мы добавили Scope и EventBus к руководству, если вы хотите прочитать об этом там: https://www.gitbook.com/book/edvin/tornadofx-guide/details –