Как я могу обновить TableView в ScalaFX? - PullRequest
0 голосов
/ 27 апреля 2018

У меня есть вид таблицы. Когда я обновляю свойства одной строки, я не вижу изменений? Например:

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

Работает нормально, но когда я обновляю имя, я не вижу этого в графическом интерфейсе.

1 Ответ

0 голосов
/ 28 апреля 2018

Во-первых, я попытаюсь обобщить то, что я вижу, и как я думаю, вы могли бы заставить это работать:

Класс 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"
...