This discussion is archived
4 Replies Latest reply: Nov 9, 2012 5:14 PM by jsmith RSS

How to create Property Editor with Listview / Tableview

972881 Newbie
Currently Being Moderated
Hello,
does someone has an example of how to implement an Object-Property-Editor, like the editors for visual components in VisualStudio (Properties Window) or Delphi (Objectinspector)?
An editable List, showing the properties of an object and offering different editing options (combobox, textfield, numberfield, ...) depending on the properties type.


I've read the documentation and the tutorials for cellfactories in Tablevies/Listviews, but all I found contained only data of the same type. I need a Tableview that handles a list with properties of different types and uses a cellfactory that creates the cell according to the items class.

I'm new to java and I hope someone can give a hint how to solve this.
Thanks in advance
Michael

Edited by: Magonline on 07.11.2012 09:25
Scene Builder was a BAD excample, because its not using a TableView or Listview.
  • 1. Re: How to create Property Editor with Listview / Tableview
    jsmith Guru
    Currently Being Moderated
    Use the DataFX project:
    http://www.javafxdata.org/

    Download the zip file from here:
    http://www.javafxdata.org/download/

    Run the samples/org/javafx/data/samples/control/TableViewFactorySample from the download.

    The TableViewFactorySample would seem to do exactly what you are requesting.
  • 2. Re: How to create Property Editor with Listview / Tableview
    972881 Newbie
    Currently Being Moderated
    Unfortunately this is also an approach of an column based object representation. Every cell in a colum shows the same type (class) of data. I need a row based solution, where every row can hold different kind of data.

    Example:

    |*Attribut*|*Value*|

    |"Enabled"|True| // Choise Box (True, False)
    |"Count"|10| // Number edit field
    |"Name"|"some Text"| // Text edit field
    |"Range"|100| // choisebox with a List of options
  • 3. Re: How to create Property Editor with Listview / Tableview
    jsmith Guru
    Currently Being Moderated
    I need a row based solution, where every row can hold different kind of data.
    Unfortunately the TableView is targeted at column based solutions rather than row based ones.

    It's still possible but you'll need to do some coding yourself.

    As you are new to Java, it may be a little difficult for you. Here are some suggestions:

    Let's say you create a class:
    class NamedProperty {
    private StringProperty name;
    private ObjectProperty value;
    // appropriate constructor getters and setters.
    }

    You can make this class the type of your TableView (e.g. TableView<NamedProperty>).

    For the Object you want to provide a property editor for, introspect on it using reflection and for each field create a NamedProperty instance added to an ObservableList.

    The ObservableList<NamedProperty> is set as the data for the TableView.

    In the cell factory for the ObjectProperty column of the TableView, based on the type of the value generate an appropriate UI control node (e.g. a TextField if the value type is String, a ComboBox if the value type is an enum, a CheckBox if the value type is a Boolean, etc.)

    First get display of the property data working with a simple name and string based value.
    Next get display of the property data working with a name and then a type specific rendered cell.
    Lastly get the editing working so that modifications of the UI controls modify the underlying object.
  • 4. Re: How to create Property Editor with Listview / Tableview
    jsmith Guru
    Currently Being Moderated
    To get you started, here's some sample code loosely based off of the TableView tutorial (http://docs.oracle.com/javafx/2/ui_controls/table-view.htm).

    The sample code property editor is deliberately limited in scope (only edits strings and booleans) as a fully fledged editor would be significantly more work.
    import java.lang.reflect.*;
    import java.util.Arrays;
    import java.util.logging.*;
    import javafx.application.Application;
    import javafx.beans.property.*;
    import javafx.beans.value.*;
    import javafx.collections.*;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.TableColumn.CellEditEvent;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.*;
    import javafx.stage.Stage;
    import javafx.util.Callback;
    
    // click in the value column (a couple of times) to edit the value in the column.
    // property editors are defined only for String and Boolean properties.
    // change focus to something else to commit the edit.
    public class TableViewPropertyEditor extends Application {
      public static void main(String[] args) { launch(args); }
      @Override public void start(Stage stage) {
        final Person aPerson = new Person("Fred", true);
        final Label currentObjectValue = new Label(aPerson.toString()); 
    
        TableView<NamedProperty> table = new TableView<>();
        table.setEditable(true);    
        
        table.setItems(createNamedProperties(aPerson));
        
        TableColumn<NamedProperty, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(new PropertyValueFactory<NamedProperty, String>("name"));
    
        TableColumn<NamedProperty, Object> valueCol = new TableColumn("Value");
        valueCol.setCellValueFactory(new PropertyValueFactory<NamedProperty, Object>("value"));
        valueCol.setCellFactory(new Callback<TableColumn<NamedProperty, Object>, TableCell<NamedProperty, Object>>() {
          @Override public TableCell<NamedProperty, Object> call(TableColumn<NamedProperty, Object> param) {
            return new EditingCell();
          }
        });
        
        valueCol.setOnEditCommit(
          new EventHandler<CellEditEvent<NamedProperty, Object>>() {
            @Override public void handle(CellEditEvent<NamedProperty, Object> t) {
              int row = t.getTablePosition().getRow();
              NamedProperty property = (NamedProperty) t.getTableView().getItems().get(row);
              property.setValue(t.getNewValue());
              
              currentObjectValue.setText(aPerson.toString());
            }
          }
        );    
        
        table.getColumns().setAll(nameCol, valueCol);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        
        VBox layout = new VBox(10);
        layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
        layout.getChildren().setAll(
          currentObjectValue,      
          table
        );
        VBox.setVgrow(table, Priority.ALWAYS);
        
        stage.setScene(new Scene(layout, 650, 600));
        stage.show();
      }
    
      private ObservableList<NamedProperty> createNamedProperties(Object object) {
        ObservableList<NamedProperty> properties = FXCollections.observableArrayList();
                
        for (Method method: object.getClass().getMethods()) {
          String name = method.getName();
          Class  type = method.getReturnType();
          if (type.getName().endsWith("Property"))  {
            try {
              properties.add(new NamedProperty(name, (Property) method.invoke(object)));
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
              Logger.getLogger(TableViewPropertyEditor.class.getName()).log(Level.SEVERE, null, ex);
            }
          }
        }
        
        return properties;
      }
      
      public class NamedProperty {
        public NamedProperty(String name, Property value) {
          nameProperty.set(name);
          valueProperty = value;
        }
        
        private StringProperty nameProperty = new SimpleStringProperty();
        public StringProperty nameProperty()  { return nameProperty; }
        public StringProperty getName() { return nameProperty; }
        public void setName(String name) { nameProperty.set(name); }
    
        private Property valueProperty;
        public Property valueProperty() { return valueProperty; }
        public Object getValue() { return valueProperty.getValue(); }
        public void setValue(Object value) { valueProperty.setValue(value); }
      }
      
      public class Person {
        private final SimpleStringProperty firstName;
        private final SimpleBooleanProperty married;
     
        private Person(String firstName, Boolean isMarried) {
          this.firstName = new SimpleStringProperty(firstName);
          this.married   = new SimpleBooleanProperty(isMarried);
        }
        
        public SimpleStringProperty firstNameProperty() { return firstName; }
        public SimpleBooleanProperty marriedProperty() { return married; }
        public String getFirstName() { return firstName.get(); }
        public void setFirstName(String fName) { firstName.set(fName); }
        public Boolean getMarried() { return married.get(); }
        public void setMarried(Boolean isMarried) { married.set(isMarried); }
        
        @Override public String toString() { return firstName.getValue() + ": " + married.getValue(); }
      }  
      
      class EditingCell extends TableCell<NamedProperty, Object> {
        private TextField textField;
        private CheckBox checkBox;
        public EditingCell() {}
    
        @Override public void startEdit() {
          if (!isEmpty()) {
            super.startEdit();
            
            if (getItem() instanceof Boolean) {
              createCheckBox();
              setText(null);
              setGraphic(checkBox);
            } else {
              createTextField();
              setText(null);
              setGraphic(textField);
              textField.selectAll();
            }  
          }
        }
    
        @Override public void cancelEdit() {
          super.cancelEdit();
    
          if (getItem() instanceof Boolean) {
            setText(getItem().toString());
          } else {
            setText((String) getItem());
          }  
          setGraphic(null);
        }
    
        @Override public void updateItem(Object item, boolean empty) {
          super.updateItem(item, empty);
    
          if (empty) {
            setText(null);
            setGraphic(null);
          } else {
            if (isEditing()) {
              if (getItem() instanceof Boolean) {
                if (checkBox != null) {
                  checkBox.setSelected(getBoolean());
                }
                setText(null);
                setGraphic(checkBox);
              } else {
                if (textField != null) {
                  textField.setText(getString());
                }
                setText(null);
                setGraphic(textField);
              }  
            } else {
              setText(getString());
              setGraphic(null);
            }
          }
        }
    
        private void createTextField() {
          textField = new TextField(getString());
          textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
          textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
              if (!newValue) {
                commitEdit(textField.getText());
              }
            }
          });
        }
    
        private void createCheckBox() {
          checkBox = new CheckBox();
          checkBox.setSelected(getBoolean());
          checkBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
          checkBox.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
              if (!newValue) {
                commitEdit(checkBox.isSelected());
              }
            }
          });
        }
    
        private String getString() {
          return getItem() == null ? "" : getItem().toString();
        }
        
        private Boolean getBoolean() {
          return getItem() == null ? false : (Boolean) getItem();
        }
      }  
    }

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points