2014-10-21 3 views
1

Я знаю Java и C#, но привязка свойств Я знаю только из C# MVVM. Я пытаюсь понять привязку свойств в JavaFX с пользовательскими getters и seters для значений свойств (например, в C#).JavaFX Binding Setter Convention

Я создал следующие классы:

public class ViewModel { 

    private StringProperty name; 

    public ViewModel() { 
     name = new SimpleStringProperty(); 
    } 

    public final String getName() { 
     return name.get(); 
    } 

    public final void setName(String name) { 
     this.name.set(name); 
    } 

    public StringProperty getNameProperty() { 
     return name; 
    } 
} 

public class Controller implements Initializable { 

    @FXML 
    private TextField nameField; 

    private final ViewModel viewModel; 

    public Controller() { 
     viewModel = new ViewModel(); 
    } 

    @Override 
    public void initialize(URL url, ResourceBundle resourceBundle) { 
     Bindings.bindBidirectional(nameField.textProperty(), 
       viewModel.getNameProperty()); 
    } 
} 

Я был под впечатлением, что, если я следовать рекомендованному JavaBean/JavaFX именования, то система привязки будет достаточно умен, чтобы использовать отражение и использование (?) мой пользовательский getter/setter для свойства. Но мой геттер/сеттер моей модели никогда не используется.

Вместо этого привязка напрямую использует свойства get/set методов без моего взаимодействия. я прочитал, что я могу использовать следующий код, но должно быть лучше, чем это:

name = new SimpleStringProperty() { 
    @Override public void set(String value) { 
     // do something... 
     super.set(value); 
    } 
}; 

Могу ли я указать, какие методы привязки следует использовать, чтобы получить/установить свои свойства? И на стороне примечания, как я могу уведомить об изменении свойства (NotifyOfPropertyChange() в C#), не меняя его?

EDIT:

То, что я пытаюсь сделать, это немного более избирательно, как к тому, что в конечном итоге быть установлен в моей собственности, так как я хотел бы использовать это значение позже, чтобы заполнить мою модель данных (здесь опущено).

В C# это тривиально, я просто устанавливаю предикат в сеттер. И я могу управлять продвижением формы/мастера, просто устанавливая другие свойства на этом пути.

public String Property { 
    get { return _property; } 
    set { 
     if(SomePredicate(value)) { 
      _property = value; 
      _nextButtonCommand.canExecute() = true; 
      // notify... 
     } 
    } 
} 
+0

Обновленный ответ в ответ на редактирование. –

ответ

3

JavaFX Свойство

Конвенции для определения name собственности в вашем ViewModel класса:

public class ViewModel { 

    private StringProperty name; 

    public ViewModel() { 
     // parameters are owning bean, property name, and initial value, 
     // and are optional for the property convention 
     name = new SimpleStringProperty(this, "name", ""); 
    } 

    public final String getName() { 
     return name.get(); 
    } 

    public final void setName(String name) { 
     this.name.set(name); 
    } 

    // Note this method name: 
    public final StringProperty nameProperty() { 
     return name; 
    } 
} 

Есть несколько вариантов: в частности, если вы хотите, чтобы сделать вещи переопределения, вы можете удалить модификатор final от nameProperty() и заменить this.name на this.nameProperty() в get и se t методов. Ключевым моментом является то, что вызов setName(...) всегда дает тот же результат, что и nameProperty().set(...) (и аналогично для методов get).

Я думаю, что ваша общая интерпретация как-то отключена на один уровень (если это имеет смысл). Класс StringProperty определяет методы get и set, и это методы, которые будут автоматически вызываться для вас из-за вашей привязки. Поэтому (даже если у вас есть несколько нестандартное соглашение об именах), ввод текста в текстовое поле будет по-прежнему обновлять значение свойства. API привязки не использует (много?) Отражение, насколько мне известно - он просто регистрирует слушателей со свойствами и обновляет их, когда другой изменяется.

Что касается пользовательских геттеров и сеттеров, то они действительно поддерживаются только для классов свойств с помощью описанной выше техники. Тем не менее, я никогда не нашел для них хороший вариант использования, особенно с API привязок, который дает вам возможность легко создавать зависимые значения.

Update

Таким образом, для вашего конкретного примера, я бы реализовать по следующим направлениям:

public class ViewModel { 
    private StringProperty name = new SimpleStringProperty(this, "name"); 
    // usual JavaFX Property methods... 
} 

Тогда там, где вам это нужно:

Predicate<String> predicate = ... ; 
BooleanBinding canExecute = Bindings.createBooleanBinding(() -> 
    predicate.test(viewModel.getName()), 
    viewModel.nameProperty()); 

, а затем вы может делать такие вещи, как

Button nextButton = new Button("Next"); 
nextButton.disableProperty().bind(canExecute.not()); 

Если предикат может измениться, вы можете даже сделать

ObjectProperty<Predicate<String>> predicate = new SimpleObjectProperty<>(s -> true); 
BooleanBinding canExecute = Bindings.createBooleanBinding(() -> 
    predicate.get().test(viewModel.getName()), 
    predicate, viewModel.nameProperty()); 

Есть много, много фабричных методов в Bindings class для создания привязок, и вы также можете создать подкласс абстрактных ObjectBinding, StringBinding и т.д. классов, если это необходимы ,

Обратите внимание, что существует тонкое изменение в философии между этим подходом и предложением, предложенным в вопросе: в подходе в вопросе (подклассификация SimpleStringProperty) логика определения, является ли действие исполняемым, удерживается свойством string. В этом подходе он учитывается в другом объекте, который наблюдает за строковым свойством (через привязку, которая фактически регистрирует WeakInvalidationListener на свойстве строки).

Уведомление

Вы можете зарегистрировать InvalidationListeners или ChangeListeners со свойством. Метод addListener(InvalidationListener) наследуется от Observable и указывает, что последнее значение, наблюдаемое в данный момент, может быть недействительным. Это позволяет «ленивые оценки» наблюдаемых, которые только вычисляют новое значение, когда оно запрашивается. Метод addListener(ChangeListener) наследуется от ObservableValue, который (как указывает его имя) является наблюдаемым, который явно переносит значение. Регистрация ChangeListener требует постоянной оценки, так как слушатель уведомляется о новом значении. API слишком тонкий для моего вкуса, хотя он обеспечивает много и много гибкости для высокопроизводительных реализаций.

Так в тестовом приложении, вы можете сделать

viewModel.nameProperty().addListener(new ChangeListener<String>() { 
    @Override 
    public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) { 
     System.out.println("Name changed from "+oldValue+" to "+newValue); 
    } 
}); 

или, в Java 8, гораздо более емким

viewModel.nameProperty().addListener((obs, oldName, newName) -> 
    System.out.println("Name changed from "+oldValue+" to "+newValue)); 

Разница между недостоверности слушателей и изменить слушателей только становится очевидным, если вы имеют ObservableValue, что зависит от вычисления. Сравните:

IntegerProperty x = new SimpleIntegerProperty(2); 
IntegerProperty y = new SimpleIntegerProperty(3); 
ObservableNumberValue sum = x.add(y); 

sum.addListener(obs -> System.out.println("Invalidated")); // invalidation listener 
x.set(3); 
y.set(5); 

с тем же кодом, но со слушателем изменения:

sum.addListener((obs, oldSum, newSum) -> System.out.println("Changed")); 

вместо недействительности слушателя.

В tutorial есть более подробная информация.