2013-05-16 5 views
10

Powiązanie z właściwościami obiektu, który sam jest zawinięty w właściwość, wydaje się być czymś, co można zrobić w typowych aplikacjach, czy jest lepszy sposób na to, aby w JavaFX działał Robię poniżej?Powiązanie z właściwościami JavaFX obiektu, który można zmienić

Kilka dodatkowych szczegółów do wyjaśnienia: Chcę utworzyć GUI w JavaFX 2.2, do zarządzania wieloma przedmiotami. Stworzyłem mały przykład, aby przetestować wszystko, w czym przedmiotami są osoby. Zestaw osób jest pokazany w niestandardowy sposób (nie lista lub drzewo, ale nie sądzę, że ma to znaczenie) i mogę wybrać jeden.

W panelu bocznym mogę edytować aktualnie wybraną osobę. Aktualizacje są natychmiast widoczne w zestawie osób, a po wybraniu innej osoby panel edycji jest aktualizowany.

Dwukierunkowe wiązanie JavaFX wydaje się idealne do tego celu. Obecnie mam to dla fx: kontroler z „osoba” Okienka edycji:

public class PersonEditor implements ChangeListener<Person> { 
    @FXML private TextField nameField; 
    @FXML private TextField ageField; 
    @FXML private TextField heightField; 

    public void setSelection(ObjectProperty<Person> selectedPersonProperty) { 
     selectedPersonProperty.addListener(this); 
    } 

    @Override 
    public void changed(ObservableValue<? extends Person> observable, Person oldVal, Person newVal) { 
     if (oldVal != null) { 
      nameField.textProperty().unbindBidirectional(oldVal.nameProperty()); 
      ageField.textProperty().unbindBidirectional(oldVal.ageProperty()); 
      heightField.textProperty().unbindBidirectional(oldVal.heightProperty()); 
     } 
     if (newVal != null) { 
      nameField.textProperty().bindBidirectional(newVal.nameProperty()); 
      ageField.textProperty().bindBidirectional(newVal.ageProperty()); 
      heightField.textProperty().bindBidirectional(newVal.heightProperty()); 
     } 
    } 
} 

Zastanawiam się, czy jest ładniejszy sposób, być może coś w JavaFX do wiążą się właściwości obiektu, które mogą zmienić ? Nie podoba mi się to, że muszę ręcznie usunąć wszystkie właściwości, czuję się jak duplikat kodu. A może to tak proste, jak może być w JavaFx?

Odpowiedz

2

Wygląda na to, że nie jest to coś, co można zrobić bardziej elegancko w JavaFX. Wiązanie i rozłączanie wydaje się być najczystszym sposobem.

Wdrożyłem jeden sposób, aby to zrobić sam. Nie jestem pewien, czy w końcu będę z niego korzystać (ponieważ zastępuje on powielanie kodu trudnym do odczytania kodem). Ale działa i jest odpowiedzią na moje własne pytanie, więc dodałem ją tutaj.

Nowa klasa PersonEditor:

public class PersonEditor implements Initializable { 
    private SelectedObjectPropertyBinder<Person> selectedObjectPropertyBinder = 
      new SelectedObjectPropertyBinder<Person>(); 

    @FXML private TextField nameField; 
    @FXML private TextField ageField; 
    @FXML private TextField heightField; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     selectedObjectPropertyBinder.getBinders().add(
      new ObjectPropertyBindHelper<Person>(nameField.textProperty()) { 
       @Override public Property objectProperty(Person p) 
       { return p.nameProperty(); } 
     }); 
     selectedObjectPropertyBinder.getBinders().add(
      new ObjectPropertyBindHelper<Person>(ageField.textProperty()) { 
       @Override public Property objectProperty(Person p) 
       { return p.ageProperty(); } 
     }); 
     selectedObjectPropertyBinder.getBinders().add(
      new ObjectPropertyBindHelper<Person>(heightField.textProperty()) { 
       @Override public Property objectProperty(Person p) 
       { return p.heightProperty(); } 
     }); 
    } 

    public void setSelection(ObjectProperty<Person> selectedPersonProperty) { 
     selectedObjectPropertyBinder. 
      setSelectedObjectProperty(selectedPersonProperty); 
    } 
} 

Klasy pomocnicze:

public class SelectedObjectPropertyBinder<T> implements ChangeListener<T> { 
    private List<ObjectPropertyBindHelper<T>> binders = 
      new ArrayList<ObjectPropertyBindHelper<T>>(); 

    public void setSelectedObjectProperty(Property<T> selectionProperty) { 
     selectionProperty.addListener(this); 
    } 

    public List<ObjectPropertyBindHelper<T>> getBinders() { 
     return binders; 
    } 

    @Override 
    public void changed(ObservableValue<? extends T> observable, 
         T oldVal, T newVal) { 
     if (oldVal != null) 
      for (ObjectPropertyBindHelper b : binders) 
       b.unbindBi(oldVal); 
     if (newVal != null) 
      for (ObjectPropertyBindHelper b : binders) 
       b.bindBi(newVal); 
    } 
} 

public abstract class ObjectPropertyBindHelper<T> { 
    private Property boundProperty; 

    public ObjectPropertyBindHelper(Property boundProperty) { 
     this.boundProperty = boundProperty; 
    } 
    public void bindBi(T o) { 
     boundProperty.bindBidirectional(objectProperty(o)); 
    } 
    public void unbindBi(T o) { 
     boundProperty.unbindBidirectional(objectProperty(o)); 
    } 
    public abstract Property objectProperty(T t); 
    public Property getBoundProperty() { 
     return boundProperty; 
    } 
} 

Jak scottb podkreślił w swojej odpowiedzi, oprawa linku nie zawsze jest to, co chcesz i tak. Jeśli chcesz mieć możliwość anulowania/zatwierdzenia zmian, możesz to zaimplementować również za pomocą czegoś podobnego (ale prawdopodobnie będzie jeszcze trudniej odczytać wynikowy kod).

3

Osobiście nie uważam, że kod, który napisałeś w swojej procedurze obsługi zdarzeń, jest taki nieporęczny lub kludgy. Jest to coś, co programy obsługi zdarzeń zwykle robią w GUI, imo.

Zadaj sobie pytanie, chociaż ... czy w twoim przypadku jest to naprawdę konieczne?

Jeśli musisz mieć aktualizacje w czasie rzeczywistym, aby zmiany wprowadzone w jednym panelu były odzwierciedlone w innym, prawdopodobnie wdrożyłeś najprostsze rozwiązanie. Są jednak trudności związane z tego rodzaju konstrukcją interfejsu użytkownika i może nie być najlepsza we wszystkich sytuacjach. Co zrobić, jeśli użytkownik musi anulować wprowadzone zmiany? Czy masz metodę cofnięcia zmian, jeśli zmienił zdanie? Czasami zmiany w czasie rzeczywistym związane z edycją nie są pożądane iw takich przypadkach wiążące obiekty modelu danych obiektom UI mogą nie być dobrym pomysłem.

+0

Hmmm, tak, widzę, że chodzi o wiązanie, które nie zawsze jest potrzebne. Ale w takim przypadku znalazłbym się w podobnej sytuacji: wprowadziłbym go, tworząc 2 metody pomocnicze, 1 metodę "selectedPersonToFields" i 1 metodę "fieldsToSelectedPerson". W obu kopiujesz dane ze zbioru pól do zestawu właściwości osoby (lub odwrotnie). Jeszcze raz, to 2 razy prawie ten sam kod! Jakoś chciałbym tego uniknąć w obu sytuacjach.Czuję, że może być bardziej elegancko. Szukam jakiejś istniejącej metody JavaFX, aby to zrobić, lub jakiegoś wzorca lub czegoś, co mogę zaimplementować, aby to zrobić ... –