首页 > 解决方案 > 如何在 ScalaFX 中更新 TableView?

问题描述

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

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 中看不到它。

标签: scalajavafxscalafx

解决方案


首先,我将尝试总结一下我所看到的,以及我认为你如何让它发挥作用:

该类通过提供一个属性来PersonView装饰一个实例,该属性被初始化为关联的字段。在“名称”列中创建每个单元格时,您会创建这样一个属性并将其与单元格的值相关联。此后,每当该属性的值发生更改时,单元格将自动更改其字段以显示该属性的新值。(顺便说一句,该属性是多余且不必要的——它提供了在属性(即绑定的属性)更改时执行一些其他操作的机会,因此单元格在执行时已经更新。)PersonfnamenamePersonitemonChangeitemfname

那么,如果您现在更改Person实例的名称Person,“名称”列中的单元格会发生什么变化?没有什么。

为什么?

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

Person添加到您的问题是从到它的关联没有关系的事实PersonView。因此,当Person name字段更改时,Person收件人无法通知其PersonView. 更糟糕的是,通过创建PersonView一个implicit类,您暗示PersonView实例本身是不重要且瞬态的,它们暂时存在只是为了Person用一组额外的方法和/或属性来装饰某个实例。

那么,我们如何才能改变事物,使它们按您的预期工作呢?Person有两种基本方法,您的选择将取决于您可以对班级施加多少控制。在这两种情况下的关键是确保StringProperty(顺便说一句,比 更好的选择ObjectProperty)包含更改的名称,Person只要 的namePerson更改...

首先,最简单的方法是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在我们控制之外更改名称。(也就是说,如果其他一些代码修改了Person实例的名称而没有经过PersonView,那么我们将对此一无所知,GUI 也不会相应地更新。)

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

现在,假设您有一个Person实例列表,您需要将它们映射到PersonView实例,然后使用后面的实例。

您的 GUI 代码现在如下所示:

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值(一旦添加到tableLinesobservable 中)现在都将具有相同的值。

但是,以下行只是更改了Person实例的名称。和PersonViewGUI没有得到更新:

someone.name = "Eric"

推荐阅读