Изменить ItemList в ComboBox в зависимости от другого выбора ComboBox

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

У меня есть набор строк в табличном представлении, каждая строка имеет разные поля со списком для столбцов. Таким образом, взаимодействие между полем со списком должно быть для каждой строки. Если в поле со списком A1 я выберу элемент 1, в поле со списком A2 список элементов будет обновлен. Моя проблема в том, что каждое поле со списком A2, B2, C2 и т. д. обновляется в соответствии с выбором в A1... то же самое с полем со списком B1, C1. Мне нужно обновить как раз А2, по А1. B2 по B1 и т.д.

Я установил поля со списком с помощью cellfactory, потому что мне нужно сохранить данные сзади в сериализуемом объекте.

Надежда ясна.

С Уважением.


person MBrownG    schedule 26.03.2014    source источник


Ответы (2)


Это совсем боль...

Из TableCell вы можете наблюдать за TableRow через его tableRowProperty().

Из TableRow вы можете наблюдать за элементом в строке через itemProperty() строки таблицы.

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

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

Bindings.select помогает управлять подобными вещами, но начиная с JavaFX 8 он печатает огромные трассировки стека на выходе в качестве предупреждений всякий раз, когда он встречает нулевое значение, которое он делает часто. Поэтому я рекомендую вам самостоятельно управлять слушателями, пока это не будет исправлено. (По какой-то причине команда JavaFX, похоже, не считает это большой проблемой, даже несмотря на то, что обнаружение нулевых значений в пути, определенном в Bindings.select, явно поддерживается в документации по API.)

Просто чтобы сделать его немного более неприятным, метод getTableRow() в TableCell<S,T> возвращает TableRow вместо более очевидного TableRow<S>. (Возможно, для этого есть причина, которую я не вижу, но...). Так что ваш код дополнительно замусорен приведениями.

Я создал пример, который работает: извиняюсь за то, что он основан на географии США, но я большая часть примера уже написана. Я действительно надеюсь, что что-то упустил и что есть более простые способы сделать это: пожалуйста, не стесняйтесь предлагать что-то, если у кого-то есть идеи получше.

И последнее замечание: библиотека EasyBind может предоставить более простой способ привязки к свойствам по произвольному пути.

person James_D    schedule 26.03.2014

Поскольку пример @James_D больше не работает из-за гниения ссылок, и я имел дело с той же проблемой, вот как я понял, как создать этот эффект.

Просмотрите полный тестовый пример здесь.

Я расширяю встроенный ComboBoxTableCell<S, T>, чтобы открыть необходимые поля. Пользовательский TableCell имеет Supplier<S> tableValue = (S) this.getTableRow().getItem();, используемый для доступа к соответствующему объекту данных. Кроме того, я рефлексивно извлекаю и сохраняю ссылку на ячейку ComboBox. Поскольку он лениво создается в суперклассе, мне также нужно установить его с помощью отражения, прежде чем я смогу его получить. Наконец, я также должен инициализировать ComboBox, как это было бы в javafx.scene.control.cell.CellUtils.createComboBox, так как я создаю его вручную. Важно раскрыть их, как:

В CellFactory столбца мы завершаем инициализацию ComboBoxCell. Нам просто нужно создать новый экземпляр нашего пользовательского ComboBoxTableCell, а затем, когда поле со списком отображается в первый раз (например, мы можем быть уверены, что у нас есть объект данных, связанный с ячейкой), мы привязываем ComboBox#itemsProperty к Bindings.When, возвращая правильный ObservableList для случая.

CellFactory:

    column1.setCellFactory(c -> {
        TransparentComboBoxTableCell<Data, Enum> tcbtc = new TransparentComboBoxTableCell<>();
        tcbtc.comboBox.setOnShown(e -> {
            if (!tcbtc.comboBox.itemsProperty().isBound()) tcbtc.comboBox.itemsProperty().bind(
                    Bindings.when(tcbtc.tableValue.get().base.isEqualTo(BASE.EVEN)).then(evens).otherwise(
                    Bindings.when(tcbtc.tableValue.get().base.isEqualTo(BASE.ODD)).then(odds).otherwise(
                    FXCollections.emptyObservableList()
                    ))
            );
        });
        return tcbtc;
    });

пользовательский ComboBoxTableCell:

public static class TransparentComboBoxTableCell<S, T> extends ComboBoxTableCell<S, T> {
    public TransparentComboBoxTableCell() {
        this(FXCollections.observableArrayList());
    }
    public TransparentComboBoxTableCell(ObservableList<T> startingItems) {
        super(startingItems);
        try {
            Field f = ComboBoxTableCell.class.getDeclaredField("comboBox");
            f.setAccessible(true);
            f.set(this, new ComboBox<>());
            comboBox = (ComboBox<T>) f.get(this);
            // Setup out of javafx.scene.control.cell.CellUtils.createComboBox
            // comboBox.converterProperty().bind(converter);
            comboBox.setMaxWidth(Double.MAX_VALUE);
            comboBox.getSelectionModel().selectedItemProperty().addListener((ov, oldValue, newValue) -> {
                if (this.isEditing()) {
                    this.commitEdit((T) newValue);
                }
            });
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
            throw new Error("Error extracting 'comboBox' from ComboBoxTableCell", ex);
        }
        tableValue = () -> (S) this.getTableRow().getItem();
    }

    public final ComboBox<T> comboBox;
    public final Supplier<S> tableValue;
}
person CAD97    schedule 23.02.2016