如何在ScalaFX中更新TableView?

时间:2018-04-27 14:03:13

标签: scala javafx scalafx

我有一个表格视图。当我更新一行的属性时,我看不到修改?例如:

implicit class PersonView(p:Person) {
  val fname = new ObjectProperty(this, "fname",p.name)
}

并在我的表格视图中

lazy val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
  columns ++= List(
    new TableColumn[PersonView, String] {
      text = "Name"
      cellValueFactory = _.value.fname
      cellFactory = { _ =>
        new TableCell[PersonView, String] {
          item.onChange { (_, _, newValue) => text = newValue }
        }
      }
    }
  )
}

它运行正常,但是当我更新名称时,我无法在GUI中看到它。

1 个答案:

答案 0 :(得分:2)

首先,我将尝试总结一下我所看到的内容,以及我认为您可以将其发挥作用:

PersonView类通过提供Person属性来装饰fname实例,该属性已初始化为关联name的{​​{1}}字段。在" Name"中创建每个单元格时在列中,您可以创建此类属性并将其与单元格的值相关联。此后,只要该属性的值发生更改,单元格将自动更改其Person字段以显示该属性的新值。 (顺便说一下,item属性是冗余且不必要的 - 当onChange属性(即绑定的item属性 - 更改时,它提供了执行某些其他操作的机会,因此单元格将在执行时更新。)

因此,如果您现在更改fname实例的名称,那么"名称"中的Person的单元格会发生什么?柱?什么都没有。

为什么?

首先,正如@James_D指出的那样,您尚未在Person实例的name与最初与之关联的Person实例的值之间建立关系。也就是说,您所做的就是更改ObjectProperty值。对于要更新的​​GUI,String的值也需要更改。

添加到您的问题是ObjectProperty与其关联的Person之间没有任何关系。因此,当PersonView Person字段发生更改时,name人无法通知其Person。更糟糕的是,通过使PersonView成为PersonView类,您建议implicit个实例本身并不重要且暂时存在,暂时仅用于装饰某些PersonView实例额外的一组方法和/或属性。

那么,我们怎样才能改变事物,使它们像你期望的那样工作?有两种基本方法,您的选择将取决于您可以对Person类施加多少控制。两种情况下的关键是确保只要Person的{​​{1}},包含StringProperty名称的ObjectProperty(比Person更好的选项)就会发生变化{1}}已更改...

首先,最简单的方法是完全取消name类。显然,您需要能够编辑Person来执行此操作;如果你不能,你将不得不尝试第二种方法。应修改PersonView以添加Person属性字段,并将Person转换为报告fname的当前值的函数:

name

在这种情况下,您的表初始化现在如下所示:

fname

现在,您可以更改// initName is the initial name of the Person, and may be changed later... class Person(initName: String, /*Whatever other arguments you require*/) { // String property storing this Person's name. Name is initialized to initName. val fname = new StringProperty(this, "fname", initName) // Report the current name of this Person. def name = fname.value // This function is not necessary, since we could change the value through fname directly // but it does look better... def name_=(newName: String): Unit = fname.value = newName } 的名称:

val tableLines = ObservableBuffer(persView) // Of Person, not PersonView!
val personTable = new TableView[Person](tableLines) {
  columns ++= List(
    new TableColumn[Person, String] {
      text = "Name"
      cellValueFactory = _.value.fname
      // No need for a cellFactory - default works fine.
    }
  )
}

一切都很好。 Person属性,val someone = new Person("Bob"/*, etc...*/) someone.name = "Fred" 字段 GUI表中相应单元格的值现在都具有相同的值。

如果您无法修改fname类型的定义,则需要第二种方法。在这里,我们使用name更改Person个实例的名称,并希望没有人更改我们控件之外的PersonView个名称。 (也就是说,如果其他一些代码修改Person实例的名称而不通过Person,那么我们对它一无所知,GUI也不会相应更新。)< / p>

Person,在这种情况下,不得PersonView类。我们希望保留PersonView实例并使用它与关联的implicit实例进行交互。 PersonView现在看起来像这样:

Person

现在,假设您有一个PersonView个实例列表,您需要将它们映射到class PersonView(p: Person) { // String property initialized to the name of the associated person. val fname = new StringProperty(this, "fname", p.name) // Change the name of the person. Note that we MUST also change the name of the // associated person instance. def name_=(newName: String): Unit = { // Change the name of the Person instance. Verify it has the value we think it has. assert(p.name == fname.value) p.name = newName // Might be p.setName(newName), etc. in your case // Change the name of our property. fname.value = newName } } 个实例,然后再使用这些实例。

您的GUI代码现在看起来像这样:

Person

现在更改人名更加复杂,因为我们需要能够找到正确的PersonView实例,但它看起来像这样:

val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
  columns ++= List(
    new TableColumn[PersonView, String] {
      text = "Name"
      cellValueFactory = _.value.fname
      // No need for a cellFactory - default works fine.
    }
  )
}

一切都好了。 PersonView属性,val someone = new Person("Bob"/*, etc...*/) val someoneView = new PersonView(someone) someoneView.name = "Fred" 字段 GUI表中相应单元格的值(PersonView.fname一次添加到Person.name可观察对象),现在都具有相同的价值。

但是,以下行只是更改someoneView实例的名称。 tableLines和GUI 不会更新:

Person