Во-первых, я попытаюсь обобщить то, что я вижу, и как я думаю, вы могли бы заставить это работать:
Класс PersonView
украшает экземпляр Person
, предоставляя свойство fname
, которое инициализируется в поле name
соответствующего Person
. При создании каждой ячейки в столбце «Имя» вы создаете такое свойство и связываете его со значением ячейки. Впредь, когда значение этого свойства изменяется, ячейка автоматически изменит свое поле item
, чтобы отобразить новое значение этого свойства. (Кстати, свойство onChange
является избыточным и ненужным & mdash; оно дает возможность выполнять некоторые другие действия при изменении свойства item
& mdash; то есть связанное свойство fname
& mdash ;, поэтому ячейка уже будет обновлена, когда это выполняется.)
Итак, если вы теперь измените имя экземпляра Person
, что произойдет с ячейкой для этого Person
в столбце «Имя»? Ничего.
Почему?
Во-первых, как указывает @James_D, вы не установили связь между name
экземпляра Person
и значением экземпляра ObjectProperty
, изначально связанного с ним. То есть все, что вы сделали, это изменили значение String
. Для обновления графического интерфейса необходимо также изменить значение этого ObjectProperty
.
К вашей проблеме добавляется тот факт, что между Person
и связанным с ним PersonView
нет никакой связи. Таким образом, когда поле Person
name
изменяется, пользователь Person
не может уведомить его PersonView
. Хуже того, делая PersonView
классом implicit
, вы предполагаете, что сами экземпляры PersonView
не важны и временны, существуют временно только для украшения некоторого экземпляра Person
дополнительным набором методов и / или свойств.
Итак, как мы можем изменить вещи так, чтобы они работали так, как вы могли ожидать? Существует два основных подхода, и ваш выбор будет зависеть от того, насколько вы можете контролировать класс Person
. Ключ в обоих случаях заключается в том, чтобы гарантировать, что StringProperty
(лучший вариант, чем ObjectProperty
, между прочим), содержащий имя Person
, изменяется при каждом изменении 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"
И все хорошо. Свойство fname
, поле name
и значение соответствующей ячейки в таблице GUI теперь будут иметь одинаковое значение.
Второй подход необходим, если вы не можете изменить определение типа Person
. Здесь мы используем PersonView
для изменения имен Person
экземпляров и надеемся, что никто не изменит Person
имен вне нашего контроля. (То есть, если какой-то другой код изменяет имя экземпляра 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
}
}
Теперь, скажем, у вас есть список 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"
И все снова хорошо. Свойство PersonView.fname
, поле Person.name
и значение соответствующей ячейки в таблице GUI (после добавления someoneView
к наблюдаемой tableLines
) теперь все будут иметь одинаковое значение .
Однако следующая строка просто меняет имя экземпляра Person
. PersonView
и графический интерфейс пользователя не обновляются:
someone.name = "Eric"