AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / user-13686027

Billie's questions

Martin Hope
Billie
Asked: 2025-04-14 19:52:56 +0800 CST

Suporte de validação do ControlsFX: mostre erros de validação no carregamento e mantenha o botão OK desabilitado até que haja uma entrada válida

  • 7

Estou trabalhando em uma aplicação JavaFX + Spring Boot usando o padrão MVVM. Usamos o ControlsFX ValidationSupport para validar campos em um formulário de login. Nosso objetivo é:

  • Mostrar ícones de erro vermelhos ("decorações") imediatamente quando a página carrega, se os campos estiverem vazios ou inválidos

  • Mantenha o botão OK desabilitado até que todas as regras de validação sejam aprovadas

Exemplo:

Temos uma caixa de diálogo para criar um novo usuário com estes campos:

  • Nome de usuário (TextField)
  • Senha (PasswordField)
  • Repita a senha (PasswordField)

Registramos Validadores assim:

@Component
public class ValidationHelper {

    public void registerUserRegistrationValidations(ValidationSupport validationSupport, TextField userName,
                                                    PasswordField password, PasswordField repeatPassword) {
        registerFocusLostValidation(userName, getEnteredUserNameDataLengthValidator(), validationSupport);
        registerFocusLostValidation(password, getEnteredPasswdDataLengthValidator(), validationSupport);
        registerFocusLostValidation(repeatPassword, getEnteredPasswdDataLengthValidator(), validationSupport);
        registerFocusLostValidation(repeatPassword, getEnteredPasswordsEqualValidator(repeatPassword), validationSupport);
    }

    private Validator<Object> getEnteredUserNameDataLengthValidator() {
        return Validator.createPredicateValidator(
                userName -> userName != null && ((String) userName).length() > 2,
                "user name too short");
    }

    private Validator<Object> getEnteredPasswdDataLengthValidator() {
        return Validator.createPredicateValidator(
                pin -> pin != null && ((String) pin).length() > 2,
                "Password too short");
    }

    private Validator<Object> getEnteredPasswordsEqualValidator(PasswordField passwdField) {
        return Validator.createPredicateValidator(
                password -> password != null && password.equals(passwdField.getText()),
                "Passwords do not match");
    }

    private <T> void registerFocusLostValidation(Control control, Validator<T> validator, ValidationSupport validationSupport) {
        validationSupport.registerValidator(control, false, validator);
    }
}

Também fazemos isso para vincular um sinalizador global:

BooleanBinding isInvalid = Bindings.createBooleanBinding(
    () -> !validationSupport.getValidationResult().getErrors().isEmpty(),
    validationSupport.validationResultProperty()
);
validationState.formInvalidProperty().bind(isInvalid);

Então no controlador de rodapé:

okButton.disableProperty().bind(validationState.formInvalidProperty());

O Problema

Isso geralmente funciona, mas somente depois que o usuário começa a digitar.

Inicialmente:

  • A página carrega sem nenhum círculo vermelho de validação
  • O botão OK fica ativo muito cedo (após digitar a primeira senha)
  • A validação para repetição de senha só é acionada após perda de foco

Queremos que a validação apareça assim que o formulário for exibido, sem interação do usuário. Tentativas

  • Tentamos Platform.runLater(validationSupport::revalidate)
  • Tentamos definir required = true em registerValidator
  • Também tentamos chamar validationSupport.getValidationResult().getErrors() manualmente, mas o estado inicial é sempre visto como "válido".

Alguma ideia de como fazer a validação ser acionada imediatamente e garantir que o botão OK se comporte corretamente desde o início?

Testado com o ControlsFX versão 11.1.1 (ou o que você estiver usando) e JavaFX 22.

Exemplo Mínimo Reproduzível

ControlsFxApp.java

package controlsFx;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;
import java.net.URL;

@SpringBootApplication
public class ControlsFxApp extends Application {
    private static final String RESOURCE = "sample.fxml";
    private ConfigurableApplicationContext springContext;

    @Override
    public void init() {
        springContext = new SpringApplicationBuilder(ControlsFxApp.class).run();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = load(RESOURCE);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public Parent load(String fxmlPath) throws IOException {
        URL location = getClass().getResource(fxmlPath);
        FXMLLoader fxmlLoader = new FXMLLoader(location);
        fxmlLoader.setControllerFactory(springContext::getBean);
        return fxmlLoader.load();
    }
}

FooterController.java

package controlsFx;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import org.springframework.stereotype.Controller;

@Controller
public class FooterController {
    private final ValidationState validationState;
    public Button register;

    public FooterController(ValidationState validationState) {
        this.validationState = validationState;
    }

    @FXML
    private void initialize() {
        register.setOnAction(event -> {
            System.out.println("Validation requested for the current step...");
        });
        register.disableProperty().bind(validationState.formInvalidProperty());
    }
}

Controlador de Registro.java

package controlsFx;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.fxml.FXML;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.decoration.GraphicValidationDecoration;
import org.springframework.stereotype.Controller;

@Controller
public class RegistrationController {
    private final ValidationHelper validationHelper;
    private final ValidationSupport validationSupport = new ValidationSupport();
    private final ValidationState validationState;
    public TextField userName;
    public PasswordField password;
    public PasswordField repeatPassword;

    public RegistrationController(ValidationHelper validationHelper, ValidationState validationState) {
        this.validationHelper = validationHelper;
        this.validationState = validationState;
    }

    @FXML
    public void initialize() {
        validationSupport.setValidationDecorator(new GraphicValidationDecoration());
        validationHelper.registerUserRegistrationValidations(validationSupport, userName, password, repeatPassword);
        Platform.runLater(() -> {
            validationSupport.revalidate();

            BooleanBinding isInvalid = Bindings.createBooleanBinding(
                    () -> !validationSupport.getValidationResult().getErrors().isEmpty(),
                    validationSupport.validationResultProperty()
            );
            validationState.formInvalidProperty().bind(isInvalid);
        });
    }
}

ValidationHelper.java

package controlsFx;

import javafx.scene.control.Control;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.springframework.stereotype.Component;

@Component
public class ValidationHelper {

    public void registerUserRegistrationValidations(ValidationSupport validationSupport, TextField userName,
                                                    PasswordField password, PasswordField repeatPassword) {
        registerFocusLostValidation(userName, getEnteredUserNameDataLengthValidator(), validationSupport);
        registerFocusLostValidation(password, getEnteredPasswdDataLengthValidator(), validationSupport);
        registerFocusLostValidation(repeatPassword, getEnteredPasswdDataLengthValidator(), validationSupport);
        registerFocusLostValidation(repeatPassword, getEnteredPasswordsEqualValidator(repeatPassword), validationSupport);
    }

    private Validator<Object> getEnteredUserNameDataLengthValidator() {
        return Validator.createPredicateValidator(
                userName -> userName != null && ((String) userName).length() > 2,
                "user name too short");
    }

    private Validator<Object> getEnteredPasswdDataLengthValidator() {
        return Validator.createPredicateValidator(
                pin -> pin != null && ((String) pin).length() > 2,
                "Password too short");
    }

    private Validator<Object> getEnteredPasswordsEqualValidator(PasswordField passwdField) {
        return Validator.createPredicateValidator(
                password -> password != null && password.equals(passwdField.getText()),
                "Passwords do not match");
    }

    private <T> void registerFocusLostValidation(Control control, Validator<T> validator, ValidationSupport validationSupport) {
        validationSupport.registerValidator(control, false, validator);
    }
}

Estado de Validação.java

package controlsFx;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import org.springframework.stereotype.Component;

@Component
public class ValidationState {
    private final BooleanProperty formInvalid = new SimpleBooleanProperty(true);

    public BooleanProperty formInvalidProperty() {
        return formInvalid;
    }
}

centro.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns:fx="http://javafx.com/fxml"
            xmlns="http://javafx.com/javafx"
            fx:controller="controlsFx.RegistrationController"
            prefHeight="400.0" prefWidth="600.0">
    <VBox AnchorPane.bottomAnchor="30" AnchorPane.leftAnchor="30" AnchorPane.rightAnchor="30"
          AnchorPane.topAnchor="30">
        <Label>User Name:</Label>
        <TextField fx:id="userName" id="userName"/>
        <Label>Password:</Label>
        <PasswordField fx:id="password" id="password"/>
        <Label>Repeat Password:</Label>
        <PasswordField fx:id="repeatPassword" id="password"/>
    </VBox>
</AnchorPane>

rodapé.xml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns:fx="http://javafx.com/fxml"
            xmlns="http://javafx.com/javafx" prefHeight="400.0" prefWidth="600.0"
            fx:controller="controlsFx.FooterController">
    <Button fx:id="register" layoutX="33.0" layoutY="187.0" prefHeight="25.0" prefWidth="534.0" text="Register"/>
</AnchorPane>

amostra.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>
<BorderPane xmlns:fx="http://javafx.com/fxml" xmlns="http://javafx.com/javafx" prefWidth="200" prefHeight="200"
            fx:id="borderPaneId">
    <top>
        <AnchorPane BorderPane.alignment="CENTER">
            <Label text="Registration">
                <font>
                    <Font size="24.0"/>
                </font>
            </Label>
            <BorderPane.margin>
                <Insets/>
            </BorderPane.margin>
        </AnchorPane>
    </top>
    <center>
        <fx:include source="center.fxml"/>
    </center>
    <bottom>
        <fx:include source="footer.fxml"/>
    </bottom>
</BorderPane>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>controlsFx</groupId>
    <artifactId>validationdemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>17</java.version>
        <javafx.version>22</javafx.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.controlsfx</groupId>
            <artifactId>controlsfx</artifactId>
            <version>11.1.2</version>
        </dependency>
    </dependencies>
</project>
java
  • 1 respostas
  • 98 Views
Martin Hope
Billie
Asked: 2024-12-04 19:23:10 +0800 CST

Redimensionando ComboBox ao dimensionar o tamanho da tela

  • 6

Gostaria de tornar meu aplicativo redimensionável. Quero redimensionar todos os elementos filhos e seu tamanho de fonte e imagens dependendo da altura e largura.

Eu segui as instruções na página a seguir: Bond Font Size and Image with Thresholds . A resposta contém duas abordagens. A primeira abordagem funciona bem, mas a segunda abordagem não redimensiona os elementos ComboBox. O que preciso mudar para que a segunda abordagem funcione para o menu suspenso de ComboBoxes também?

O código de exemplo é fornecido aqui, Bond Font Size e Image with Thresholds também.

css
  • 1 respostas
  • 48 Views

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve