[英]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
实例,该属性被初始化为关联的Person
的name
字段。 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.