Как проверить данные, прежде чем разрешить изменение выбора? - PullRequest
0 голосов
/ 16 октября 2018

В моем приложении есть два раздела: список данных и раздел для редактирования деталей этих данных.Пользователь выбирает элемент из ListView или TableView, и свойства этого элемента появляются и редактируются в правой части окна. MCVE ниже продемонстрирует это.

Что мне нужно сделать, это проверить значения этих элементов управления «редактор» перед изменением выбора в ListView.


MCVE:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        HBox root = new HBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        // Simple ListView
        ListView<Person> listView = new ListView<>();
        listView.getItems().setAll(
                new Person("Jack", "j@email.com"),
                new Person("Bill", "bill@email.com"),
                new Person("Diane", "dd@email.com")
        );

        // TextFields to edit values
        TextField txtName = new TextField();
        TextField txtEmail = new TextField();

        // Add controls to the root layout
        root.getChildren().addAll(
                listView,
                new VBox() {{
                    getChildren().addAll(
                            new Label("Name:"),
                            txtName,
                            new Label("Email:"),
                            txtEmail);
                }}
        );

        // Add listener to update bindings for the TextFields
        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {

            // Unbind previously bound values
            if (oldValue != null) {

                // If name or email are missing, prevent the change
                if (!validate(oldValue))
                    return; // *** This is where I need help as this obviously is not correct *** //

                txtName.textProperty().unbindBidirectional(oldValue.nameProperty());
                txtEmail.textProperty().unbindBidirectional(oldValue.emailProperty());
            }

            // Bind the new values
            if (newValue != null) {
                txtName.textProperty().bindBidirectional(newValue.nameProperty());
                txtEmail.textProperty().bindBidirectional(newValue.emailProperty());

                // Refresh the ListView to show changes
                listView.refresh();
            }

        });

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }

    private boolean validate(Person oldValue) {
        return !(oldValue.getName().trim().isEmpty() || oldValue.getName().trim().isEmpty());
    }
}

class Person {

    private StringProperty name = new SimpleStringProperty();
    private StringProperty email = new SimpleStringProperty();

    public Person(String name, String email) {
        this.name.set(name);
        this.email.set(email);
    }

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

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

    public StringProperty nameProperty() {
        return name;
    }

    public String getEmail() {
        return email.get();
    }

    public void setEmail(String email) {
        this.email.set(email);
    }

    public StringProperty emailProperty() {
        return email;
    }

    @Override
    public String toString() {
        return name.get();
    }
}


Когда пользователь выбирает другой элемент из ListView, как мне сначала проверить, что txtName и txtEmail содержатдопустимые значения?

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

По сути, в этом примере я хочу убедиться, что текстовые поля не пусты, прежде чем перейти к новому выделению.

Ответы [ 3 ]

0 голосов
/ 17 октября 2018

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

Я изменил реализацию слушателя ниже.И это работает в вашем демо, как и ожидалось.Единственное, что меня беспокоит, это использование Platform.runLater.Может быть, кто-то может подсказать мне, как получить без этого.

// Add listener to update bindings for the TextFields
        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            boolean proceed = true;
            // Unbind previously bound values
            if (oldValue != null) {
                txtName.textProperty().unbindBidirectional(oldValue.nameProperty());
                txtEmail.textProperty().unbindBidirectional(oldValue.emailProperty());

                // If name or email are missing, prevent the change
                if (validate(oldValue)) {
                    Platform.runLater(() -> listView.getSelectionModel().select(oldValue));
                    proceed = false;
                }
            }

            // Bind the new values
            if (newValue != null && proceed) {
                txtName.textProperty().bindBidirectional(newValue.nameProperty());
                txtEmail.textProperty().bindBidirectional(newValue.emailProperty());
                // Refresh the ListView to show changes
                listView.refresh();
            }
        });

Обновление: Arrggh ... Я только что заметил, что мой ответ почти похож на связанный ответ, и вы уже обсуждаличто с @Kleopatra.Но это решит вашу проблему:)

@ Kleopatra: В соответствии с вашим комментарием относительно refresh (), не могли бы вы предложить способ обновления информации в listView.Я застрял в этой точке.

0 голосов
/ 17 октября 2018

Это не ответ на исходный вопрос, просто фрагмент кода слишком длинный для комментария.

Re: refresh ();

Почемуне используйте его : это абсолютный аварийный API, грубая ловушка для принудительного обновления, если нет способа заставить автоматику свойств FX выполнять свою работу.Не может повторяться достаточно часто: не делайте этого - не нужно - если вам, кажется, это нужно, что-то не так с вашей настройкой данных.Это факт, всегда (как в 99,999999%)

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

Фрагмент кода:

ObservableList<Person> persons = FXCollections.observableArrayList(
        p -> new Observable[]{p.nameProperty(), p.emailProperty()});
persons.setAll(new Person("Jack", "j@email.com"),
        new Person("Bill", "bill@email.com"),
        new Person("Diane", "dd@email.com"));

// Simple ListView
ListView<Person> listView = new ListView<>(persons);
0 голосов
/ 16 октября 2018

Как я знаю, вы не можете использовать это событие внутри ChangeListener, так как он слушатель, он уведомляет вас о уже произошедших изменениях.

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

  listView.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
        if(!validate(listView.getSelectionModel().getSelectedItem())) event.consume();
    });

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...