简体   繁体   English

如何在ScalaFX中更新TableView?

[英]How can I update TableView in ScalaFX?

I have a table view. 我有一张桌子。 When I update the properties of one row, I can not see the modifications? 更新一行的属性时,看不到修改吗? For example: 例如:

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

and in my table view 在我的表格视图中

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

It works fine, but when I update the name, I can not see that in GUI. 它工作正常,但是当我更新名称时,在GUI中看不到它。

Firstly, I'll attempt to summarize what I'm seeing, and how I think you might get this to work: 首先,我将尝试总结我所看到的内容,以及我认为您可能如何使它起作用:

The PersonView class decorates a Person instance by providing an fname property, that is initialized to the name field of the associated Person . PersonView类通过提供fname属性来装饰Person实例,该属性被初始化为关联的Personname字段。 When creating each cell in the "Name" column, you create such a property and associate it with the value of the cell. 在“名称”(Name)列中创建每个单元格时,将创建一个这样的属性,并将其与该单元格的值相关联。 Henceforth, whenever the value of that property changes, the cell will automatically change its item field to show the new value of that property. 此后,只要该属性的值发生更改,单元格就会自动更改其item字段以显示该属性的新值。 (BTW, the onChange property is redundant and unnecessary—it provides an opportunity to perform some other actions when the item property—that is, the bound fname property—changes, so the cell will have already been updated when it executes.) (顺便说一句, onChange属性是多余且不必要的,它在item属性(即绑定的fname属性)发生更改时提供了执行一些其他操作的机会,因此该单元在执行时已被更新。)

So, if you now change the name of a Person instance, what happens to the cell for that Person in the "Name" column? 因此,如果您现在更改一个Person实例的名称,那么在“名称”列中该Person的单元格将如何处理? Nothing. 没有。

Why? 为什么?

Firstly, as @James_D points out, you have not established a relationship between the name of a Person instance, and the value of the ObjectProperty instance originally associated with it. 首先,正如@James_D所指出的,您尚未在Person实例的name和最初与其关联的ObjectProperty实例的值之间建立关系。 That is, all you've done is change a String value. 也就是说,您要做的就是更改String值。 For the GUI to be updated, the value of that ObjectProperty needs to change too. 为了更新GUI,该ObjectProperty的值也需要更改。

Adding to your problem is the fact that there is no relationship from the Person to its associated PersonView . 问题还增加了一个事实,那就是从Person到与其关联的PersonView之间没有任何关系。 So, when the Person name field is changed, there's no way for the Person to person to notify its PersonView . 所以,当Person name域改变,有没有办法对Person对人,通知其PersonView Worse, by making PersonView an implicit class, you're suggesting that PersonView instances themselves are unimportant and transient, existing temporarily solely to decorate some Person instance with an additional set of methods and/or properties. 更糟糕的是,通过使PersonView成为implicit类,您建议PersonView实例本身并不重要且是暂时的,它们暂时存在只是为了用一组额外的方法和/或属性来装饰某些Person实例。

So, how can we change things so that they work as you might expect? 那么,我们如何改变事物以使它们按您期望的那样工作? There are two basic approaches, and your choice will depend upon how much control you can exert on the Person class. 有两种基本方法,您的选择将取决于您可以对Person类施加多少控制。 The key in both cases is to ensure that the StringProperty (a better option than an ObjectProperty , incidentally) containing the name of the Person changes whenever the name of the Person is changed... 在这两种情况下,关键是要确保StringProperty (比一个更好的选择ObjectProperty包含的名字,顺便说一句) Person只要改变name的的Person改变...

Firstly, the simplest method is to do away with PersonView class altogether. 首先,最简单的方法是完全取消PersonView类。 Clearly, you'll need to be able to edit Person to do this; 显然,您需要能够编辑Person来执行此操作; if you cannot, you'll have to try the second approach. 如果不能,则必须尝试第二种方法。 Person should be modified to add an fname property field, with name being converted to a function that reports the current value of fname : 应该修改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
}

In this case, your table initialization now looks like this: 在这种情况下,您的表初始化现在看起来像这样:

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

Now, you can change the name of a Person like this: 现在,您可以像这样更改一个Person的名字:

val someone = new Person("Bob"/*, etc...*/)
someone.name = "Fred"

And all is good. 一切都很好。 The fname property, the name field and the value of the corresponding cell in the GUI table, will now all have the same value. 现在, fname属性, name字段 GUI表中相应单元格的值都将具有相同的值。

The second approach is required if you cannot modify the definition of the Person type. 如果您无法修改Person类型的定义,则需要第二种方法。 Here, we use PersonView to change the names of Person instances, and hope that no-one changes Person names outside of our control. 在这里,我们使用PersonView来更改Person实例的名称,并希望没有Person在我们的控制范围之外更改Person名称。 (That is, if some other code modifies the name of a Person instance without going through PersonView , then we'll know nothing about it, and the GUI will not be updated accordingly.) (也就是说,如果其他一些代码无需经过PersonView即可修改Person实例的名称,那么我们将一无所知,并且GUI也不会相应地更新。)

PersonView , in this case, must not be an implicit class. 在这种情况下, PersonView 不得implicit类。 We want to retain a PersonView instance and use it to interact with an associated Person instance. 我们想要保留一个PersonView实例,并使用它与关联的Person实例进行交互。 PersonView now looks like this: 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
  }
}

Now, say you have a list of Person instances, you'll need to map them to PersonView instances, and use those latter instances subsequently. 现在,假设您有一个Person实例列表,则需要将它们映射到PersonView实例,并随后使用这些实例。

Your GUI code now looks like this: 您的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.
    }
  )
}

Changing the names of people is now a little more complex, because we need to be able to find the right PersonView instance, but it would look like this: 现在,更改人员的姓名稍微有些复杂,因为我们需要能够找到正确的PersonView实例,但是它看起来像这样:

val someone = new Person("Bob"/*, etc...*/)
val someoneView = new PersonView(someone)
someoneView.name = "Fred"

And all is good once again. 一切再次美好。 The PersonView.fname property, the Person.name field and the value of the corresponding cell in the GUI table (once someoneView is added to the tableLines observable), will now all have the same value. 现在, PersonView.fname属性, Person.name字段 GUI表中相应单元格的值(将someoneView添加到可观察的tableLines )现在都将具有相同的值。

However, the following line just changes the name of a Person instance. 但是,以下行仅更改Person实例的名称。 The PersonView and GUI do not get updated: PersonView和GUI 不会更新:

someone.name = "Eric"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM