Die Ant­wort auf die Fra­ge, war­um es eine gute Idee ist qua­li­ta­tiv hoch­wer­ti­ge Soft­ware her­zu­stel­len, ist in sich logisch und für alle Par­tei­en des Ent­wick­lungs­pro­zes­ses leicht ver­ständ­lich.

Klar ist: Alle Kun­den möch­ten natür­lich ger­ne ein qua­li­ta­tiv hoch­wer­ti­ges Soft­ware­pro­dukt, dass allen Anfor­de­run­gen ent­spricht. Es soll­te eben­falls bil­li­ger sein, zum fest­ge­leg­ten Zeit­punkt lie­fer­bar sein, zukünf­ti­ge War­tungs­kos­ten, Pro­blem­kos­ten und Feh­ler aber gleich­zei­tig aus­spa­ren. Die Anfor­de­run­gen sind also hoch. Auf der ande­ren Sei­te ver­su­chen Soft­ware-Her­stel­ler, ihre Pro­duk­te recht­zei­tig und in hoher Qua­li­tät her­zu­stel­len, um ihre eige­nen Kos­ten für die Garan­tie­pha­se zu mini­mie­ren. Des­we­gen ver­fol­gen bei­de Par­tei­en des Soft­ware­ent­wick­lungs­pro­zes­ses min­des­tens zwei iden­ti­sche Zie­le: Das Pro­dukt soll recht­zei­tig zu Ver­fü­gung gestellt wer­den und es muss die höchs­te denk­ba­re Soft­ware­qua­li­täts­stu­fe errei­chen.

Wel­che Fol­gen hat also die Erschaf­fung qua­li­ta­tiv hoch­wer­ti­ger Soft­ware­pro­duk­te?

Zunächst erhöht sie die Chan­cen auf eine erfolg­rei­che Pro­jekt­ab­wick­lung und des­sen recht­zei­ti­ger Fer­tig­stel­lung. Das führt in der Kon­se­quenz zu einem effi­zi­en­ten Start der Soft­ware. Die Garan­tie­kos­ten wer­den für bei­de Par­tei­en gesenkt. Die­ser Vor­teil der Kos­ten­sen­kung ist nicht hoch genug ein­zu­schät­zen im heu­ti­gen kos­ten­ori­en­tier­ten Unter­neh­mer­da­sein. Die gene­rel­le Ent­wick­lungs­zeit eines Pro­duk­tes wird redu­ziert, da die Ent­wick­ler sich nicht mit Bugs und Feh­lern her­um­schla­gen müs­sen. Es kann also ziel­ge­rich­tet und effi­zi­ent gear­bei­tet wer­den.

Aus der Per­spek­ti­ve des Soft­ware­ent­wick­lungs­un­ter­neh­mens gibt es direk­te und indi­rek­te Grün­de dafür, qua­li­ta­tiv hoch­wer­ti­ge Soft­ware her­zu­stel­len. Wel­che Effek­te las­sen ich für das Unter­neh­men dabei fest­stel­len?

Die Nut­zung von qua­li­ta­tiv hoch­wer­ti­ger Soft­ware­pro­duk­te redu­ziert die inter­nen Ent­wick­lungs­kos­ten und die erhöht die Durch­füh­rungs­ge­schwin­dig­keit der ver­schie­de­nen Auf­ga­ben im Arbeits­pro­zess. So wird bei­spiels­wei­se ein tota­ler Arbeits­stopp wegen Soft­ware­pro­ble­men ver­hin­dert, ein Wech­sel zu ande­ren Auf­ga­ben­stel­lun­gen ver­mie­den, eine Bug­su­che durch­ge­führt, ein Gegen­check von Ent­schei­dun­gen sicher­ge­stellt, eine Suche für Umge­hungs­op­tio­nen ein­ge­setzt und eine Wah­rung der Leis­tungs­fä­hig­keit erwirkt. All dies ist für ein Unter­neh­men bares Geld wert. Wei­ter­hin wird der Kun­de nun an das Unter­neh­men gebun­den, so lan­ge sei­ne Zufrie­den­heit mit den Pro­duk­ten im Vor­der­grund steht. Zuletzt stellt sie einen schier unüber­wind­li­chen Wett­be­werbs­vor­teil gegen­über der Kon­kur­renz dar, wenn es dar­um geht neue Auf­trags­aus­schrei­bun­gen für Soft­ware­ent­wick­lung zu gewin­nen.

Eine Redu­zie­rung von Bugs in einem Pro­gramm spricht zwei­fel­los für die Kom­pe­tenz des Soft­ware-Ent­wick­lers. Die Zufrie­den­heit des Kun­den wird deut­lich gestei­gert, eben­so wie die Wahr­schein­lich­keit zukünf­ti­ger Auf­trä­ge, was für das Unter­neh­men ent­schei­dend sein kann. Oben­drein wer­den auch posi­ti­ve Emp­feh­lun­gen für das Unter­neh­men wei­ter­ge­ge­ben, was einen neu­en Kun­den­pool erschlie­ßen hel­fen kann.

Der letz­te Punkt sind die Upgrade-Mög­lich­kei­ten, die eine Soft­ware wider­stands­fä­hi­ger gegen neue Kon­kur­renz­pro­duk­te machen. Der Kun­de muss die alte Soft­ware nicht erset­zen, son­dern behält sie auf höchs­tem Stan­dard.

Intech­core auf dem 25. Deut­scher EDV-Gerichts­tag 2016. Gemein­sam mit unse­ren Part­nern im Bereich Soft­ware für die Jus­tiz haben wir die zukünf­ti­ge Lösung forumSTAR-Text Moder­ni­sie­rung prä­sen­tiert.

Ach­tung, seit Janu­ar 2016 haben wir neue Adres­se:

Intech­core GmbH
Kel­ten­ring 17
82041 Ober­ha­ching bei Mün­chen

Die heu­ti­ge Mes­se läuft unter dem Mot­to “E-Jus­ti­ce — Jus­tiz unter Strom”. Intech­core ist als Soft­ware­ent­wick­lungs­fir­ma mit unse­rem Stand dabei.

Das Foto folgt gleich nach dem Auf­bau des Stan­des.

Wir sind vom 23. bis 25.09.2015 auf dem 24. Deut­schen EDV-Gerichts­tag in Saar­brü­cken.

 

Wir freu­en uns, am 23. EDV-Gerichts­tag im Sep­tem­ber 2014 in Saar­brü­cken teil­zu­neh­men.

In die­sem Bei­trag möch­ten wir eini­ge Vor­tei­le der Java­FX-Biblio­thek erläu­tern, die beson­ders im Bezug auf das User­in­ter­face eine Rol­le spielt.
Als einer der wich­ti­gen Vor­tei­le der Java­FX Biblio­thek gilt die Mög­lich­keit der Nut­zung von Styles (CSS), die uns aus dem WWW bekannt sind.
Dadurch ver­liert sich die Abhän­gig­keit zu der Biblio­thek Look & Feel. Man kann jedoch selbst das Aus­se­hen der Appli­ka­ti­on bestim­men, was auch ziem­lich fle­xi­bel und schön mög­lich ist. Die Gestal­tung kann dyna­misch erfol­gen, eine Ani­ma­ti­on beinhal­ten oder auch 3D Gra­fik.
Durch die Benut­zung der Style-Kon­zep­ti­on ist es nun mög­lich, die Appli­ka­tio­nen mit so genann­ten “Skins” zu erstel­len, durch die das Aus­se­hen der Appli­ka­ti­on kom­plett von der Busi­ness­lo­gik los­ge­löst wird und mehr Indi­vi­du­al­tät erhält. Sol­che indi­vi­du­el­len Skins kön­nen sogar sepa­rat von einem Desi­gner erstellt wer­den.

Erstel­len wir ein ein­fa­ches Bei­spiel eines Dia­log­fens­ters mit einem But­ton:

public class JavaFXDialog1 extends Application {
    @Override
    public void start(Stage stage) {
        final VBox vbox = new VBox();
        final Button button = new Button("test");
        vbox.getChildren().addAll(button);
        final Scene scene = new Scene(vbox, 150, 100);
        stage.setScene(scene);
        stage.show();
    }
  
    public static void main(String[] args) {
        launch(args);
    }

avafx and css styles 1

Styles kann man unter­schied­lich anwen­den:
1) Unmit­tel­bar im Code, um z.B. die Schrift­far­be in dem But­ton zu ändern:

button.setStyle("-fx-text-fill: red");

avafx and css styles 2

2) Mit Hil­fe einer CSS-Datei, auf die die Klas­se Sce­ne aus­ge­rich­tet wer­den soll:
Dafür wird eine Datei mit der Erwei­te­rung .css erstellt und unter dem Pro­jekt­ver­zeich­nis abge­legt, z.B. /css/styles.css.
Inhalt der Datei:

.button {
    -fx-text-fill: blue;
}

Dabei ist es sehr wich­tig, die Ent­wick­lungs­um­ge­bung so ein­zu­rich­ten, dass sie die­se CSS-Datei­en beim Bil­den der Appli­ka­ti­on auch mit­ko­piert.
Unter Intel­liJ IDEA wird es bei­spiels­wei­se fol­gen­der­ma­ßen gemacht:

avafx and css styles 3

Nun ist alles fer­tig, um die Style-Datei ein­zu­bin­den:

scene.getStylesheets().add((getClass().getResource("/css/styles.css")).toExternalForm());

Wir star­ten das Pro­jekt und bekom­men fol­gen­des Dia­log­fens­ter:
avafx and css styles 4

Instruk­ti­on .but­ton in der CSS-Datei sagt aus, dass nun alle Knöp­fe eine blaue Schrift­far­be haben wer­den:

final Button button1 = new Button("button1");
final Button button2 = new Button("button2");
vbox.getChildren().addAll(button1, button2);

avafx and css styles 5

Und was, wenn das nicht das ist, was wir brau­chen? Was, wenn wir einen kon­kre­ten Knopf defi­nie­ren wol­len?
3) Abhil­fe schafft die user­be­zo­ge­ne Defi­ni­ti­on des Knopf-Styles:
In styles.css schrei­ben wir:

.button1 {
    -fx-text-fill: green;
}

Und im Code:

button1.getStyleClass().add("button1");

Das Dia­log­fens­ter sieht so aus:
avafx and css styles 6

Nun haben alle Knöp­fe, die mit der Style-Klas­se ver­bun­den sind, grü­ne Schrift­far­be, wobei die Metho­de add() uns dabei Hin­wei­se gibt, dass wir meh­re­re von sol­chen Styles hin­zu­fü­gen kön­nen, wodurch ver­schie­de­ne Ele­ment­ei­gen­schaf­ten erwei­tert, vor­de­fi­niert oder über­la­den wer­den.
4) User-Style kann man auch durch den so genann­ten ID defi­nie­ren:

In der styles.css schrei­ben wir:

#button2 {
    -fx-text-fill: yellow;
}

Und im Code:

button2.setId("button2");

Als Ergeb­nis bekom­men wir fol­gen­des Dia­log­fens­ter:

avafx and css styles 7

D.h. alle Ele­men­te mit der glei­chen ID sehen gleich aus.

Was kann man sonst noch Inter­es­san­tes mit den Styles machen?

Styles kön­nen auch die so genann­ten Ver­hal­tens­trig­ger bear­bei­ten, die aus der XAML Welt kom­men.
Wenn wir bei dem Bei­spiel mit dem Knopf blei­ben, gehö­ren zu die­sen Trig­gern sol­che Ereig­nis­se der GUI wie Fokus, Selek­ti­on, Mou­se­down, Mou­seo­ver usw. also alles, was man nicht im Dia­log­fens­ter haben möch­te. Es beinhal­tet nur den Bui­siness­lo­gin bei den kun­den­sei­ti­gen Ände­rungs­wün­schen und es wird nur die CSS-Datei geän­dert und nicht die Logik.
So kann man z.B. mit der fol­gen­den CSS-Defi­ni­ti­on die Far­be des Knop­fes ändern, wenn der User mit der Maus dar­über fährt.

.button:hover {
    -fx-background-color: orange;
}

So sieht das Dia­log­fens­ter bei Mou­seo­ver aus:

avafx and css styles 8

Wie bereits oben erwähnt, führt das zum glei­chen Ver­hal­ten von allen But­ton-Klas­sen. Mit die­sem Code:

.button1:hover {
    -fx-background-color: orange;
}

wer­den die Trig­ger nur für die Ele­men­te ange­wen­det, die auf die Klas­se «button1» ver­wei­sen.

Die­se Vor­ge­hens­wei­se kann man bei vie­len Trig­ger anwen­den, bei Knöp­fen gibt es z.B. auch noch fou­sed, selec­ted oder pres­set.

Lei­der kann das nicht direkt im Code genutzt wer­den:

button.setStyle(":hover -fx-text-fill: red");

Viel­leicht wird die­se Mög­lich­keit in der Zukunft durch Java­FX Deve­lo­per rea­li­siert.

Wozu brau­chen wir das alles? Im Inter­net kann man noch eine Rei­he an Bei­spie­len fin­den, die deut­li­cher sind als die­ses. Das Ziel des Bei­trags war nicht, die­se zu kopie­ren. Uns geht es dar­um, dass die Kon­zep­ti­on von Syles und Trig­ger  auch für den eige­nen Bedarf erwei­tert wer­den kann, das ist für uns von Inter­es­se.

Als Bei­spiel kön­nen wir fol­gen­de Aus­gangs­si­tua­ti­on betrach­ten:
wir wol­len mit Java­FX eine visu­el­le Kom­po­nen­te umset­zen, die für die Aus­wahl der Grö­ße einer im Text ein­ge­füg­ten Tabel­le die­nen soll. Das Aus­se­hen der Kom­po­nen­te, das Farb­sche­ma, die Grö­ße usw., sol­len dabei kein Bestand­teil der Busi­ness­lo­gik der Kom­po­nen­te sein, son­dern über eine exter­ne CSS-Datei kon­fi­gu­rier­bar sein.

Das soll­te unge­fähr so aus­se­hen:

avafx and css styles 9

Der User geht mit dem Maus­zei­ger über die Tabel­le und sieht sei­ne aus­ge­wähl­ten Ele­men­te, in dem Fall eine Tabel­le mit 7 x 8 Zel­len. Beim Klick auf die Kom­po­nen­te soll­te die Aus­wahl an das Pro­gramm über­mit­telt wer­den, um eine ent­spre­chen­de Tabel­le ein­zu­fü­gen.
Sicher­lich kann man auf die Aus­wahl über den Code reagie­ren, aber was wenn der eine Kun­de eine bestimm­te Far­be bevor­zugt, der ande­re aber ganz ande­re Far­ben bevor­zugt? Mög­lich ist auch, dass das Farb­sche­ma durch ein «Skin» oder sonst wie vor­de­fi­niert wird — was dann?

Hier wird nur eine Kom­po­nen­te für die Zel­len­se­lek­ti­on benö­tigt, um deren Aus­se­hen es an die­ser Stel­le aber nicht gehen soll.

Hier kön­nen wir auf das vor­an­ge­gan­ge­ne Bei­spiel mit dem Trig­ger «hover» für den But­ton anknüp­fen. Der wirkt aller­dings für jede Zel­le, wenn man mit dem Maus­zei­ger über sie fährt. Die Zel­len, die der Maus­zei­ger ver­las­sen hat, ent­spre­chen dem Trig­ger nicht mehr und fal­len somit aus der Aus­wahl. Wie kön­nen wir den gesam­ten Bereich aus­ge­wählt behal­ten?

Für die Lösung die­ser Auf­ga­be wird ein eige­ner Trig­ger erstellt, der auf eine bestimm­te Eigen­schaft des Objek­tes reagiert, z.B. «bin im Dia­pa­son der Selek­ti­on» oder inRange, die wir fol­gen­der­ma­ßen in der CSS-Datei defi­nie­ren kön­nen:

.MyCell:inRange {
    -fx-border-width: 0.5;
    -fx-border-color: #ffffff;
    -fx-background-color: lightskyblue
}

An der Stel­le muss man aber sagen, dass dies ist kei­ne ein­fa­che Auf­ga­be ist. Zudem ist die Lösung für die Java­FX Ver­sio­nen 1.7 und 1.8 gänz­lich unter­schied­lich. Für die Lösung in der Ver­si­on 1.7. sind wir auf die Nut­zung einer Men­ge der depre­ca­ted-Metho­den ange­wie­sen.

Für den Anfang schau­en wir uns die Kom­po­nen­te an:

public class JavaFXDialog2  extends Application {
    @Override
    public void start(Stage stage) {
        final VBox vbox = new VBox();
        final GridPaneEx table = new GridPaneEx();
        table.init(10, 10);
        final Label label = new Label();
        label.setMaxWidth(Double.MAX_VALUE);
        label.setAlignment(Pos.CENTER);
        label.setTextAlignment(TextAlignment.CENTER);
        label.setStyle("-fx-padding: 3 0 5 0");
        label.textProperty().bind(table.text);
        vbox.getChildren().addAll(label, table);
        final Scene scene = new Scene(vbox, 350, 300);
        scene.getStylesheets().add((getClass().getResource("/css/styles.css")).toExternalForm());
        scene.setFill(null);
        stage.setScene(scene);
        stage.show();
    }
 
 
    public static void main(String[] args) {
        launch(args);
    }
 
 
    private void fireCreateTable(final int cols, final int rows){
        System.out.println("cols = " + cols + ", rows = " + rows);
    }
 
 
    protected class GridPaneEx extends GridPane {
  
        public final StringProperty text = new SimpleStringProperty("cancel");
        private int cols;
        private int rows;
  
        public GridPaneEx(){
            this.setOnMouseExited(new EventHandler() {
                @Override
                public void handle(MouseEvent mouseEvent) {
                    text.setValue("cancel");
                    deselectAll();
                }
            });
        }
  
        public void init(final int cols, final int rows){
            getChildren().clear();
            this.cols = cols;
            this.rows = rows;
            for (int col = 0; col < cols; col++){
                for (int row = 0; row < rows; row++){
                    final Button rect = new Button();
                    rect.setMinSize(30, 10);
                    add(rect, col, row);
                    final int selectedCol = col;
                    final int selectedRow = row;
                    rect.setOnMouseMoved(new EventHandler() {
                        @Override
                        public void handle(MouseEvent mouseEvent) {
                            selectRange(selectedCol, selectedRow);
                            text.setValue((selectedCol + 1) + " x " + (selectedRow + 1));
                        }
                    });
                    rect.setOnAction(new EventHandler() {
                        @Override
                        public void handle(ActionEvent actionEvent) {
                            fireCreateTable(selectedCol + 1, selectedRow + 1);
                            deselectAll();
                        }
                    });
                }
            }
            deselectAll();
        }
  
        private Node getNodeFromGridPane(int col, int row) {
            for (Node node : getChildren()) {
                if (GridPane.getColumnIndex(node) == col && GridPane.getRowIndex(node) == row) {
                    return node;
                }
            }
            return null;
        }
  
        private void selectCell(int col, int row, final boolean select){
            final Node node = getNodeFromGridPane(col, row);
            if (select){
                node.setStyle("-fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue");
            } else {
                node.setStyle("-fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff");
            }
        }
  
        public void deselectAll(){
            for (int col = 0; col < cols; col++){
                for (int row = 0; row < rows; row++){
                    selectCell(col, row, false);
                }
            }
        }
        private void selectRange(int selectedCol, int selectedRow){
            deselectAll();
            for (int col = 0; col <= selectedCol; col++){
                for (int row = 0; row <= selectedRow; row++){
                    selectCell(col, row, true);
                }
            }
        }
    }
}

Beson­ders inter­es­sant ist die Metho­de selec­t­Cell, in der die Zel­len­fär­bung unmit­tel­bar im Code rea­li­siert wird:

Für nor­ma­le Zel­len:

node.setStyle("-fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff");

Für Zel­len im aus­ge­wähl­ten Bereich:

node.setStyle("-fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue");

Weil es in der Auf­ga­ben­be­schrei­bung steht, dass kla­re Far­ben­zu­wei­sung unmög­lich ist, ver­su­chen wir die mit Hil­fe eines eige­nen Styles in styles.css zu defi­nie­ren:

#MyCellNormal {
    -fx-border-width: 0.5;
    -fx-border-color: #000000;
    -fx-background-color: #ffffff;
}
 
 
#MyCellInRange {
    -fx-border-width: 0.5;
    -fx-border-color: #ffffff;
    -fx-background-color: lightskyblue
}

Und in der Metho­de selec­t­Cell:

private void selectCell(int col, int row, final boolean select){
            final Node node = getNodeFromGridPane(col, row);
            if (select){
                                node.setId("MyCellNormal");
            } else {
                                node.setId("MyCellInRange");
            }
        }

Schon bes­ser, nicht war? D.h. wenn der Kun­de mit der Farb­wahl unzu­frie­den sein soll­te, kann man sie direkt in der Datei styles.css ändern. Die Logik der Kom­po­nen­te bleibt dabei unver­än­dert.

Aller­dings gibt es eine noch ele­gan­te­re Lösung der Auf­ga­be: Plat­zie­ren wir in styles.css noch zusätz­lich 2 Syt­les:

.MyCell {
    -fx-border-width: 0.5;
    -fx-border-color: #000000;
    -fx-background-color: #ffffff;
}
 
 
.MyCell:inRange {
    -fx-border-width: 0.5;
    -fx-border-color: #ffffff;
    -fx-background-color: lightskyblue
}

Das bedeu­tet, dass die Zel­len sich auf den Style «MyCell» ori­en­tie­ren und wenn der Trig­ger «inRange» greift, ana­log zu «hover» oder «pres­set», ändert sich die Far­be ent­spre­chend.
Aber wie brin­gen wir der Zel­le bei, den Trig­ger zu star­ten?
Da wir in unse­rem Bei­spiel für die Zel­len, But­ton-Ele­men­te nut­zen, ist es erfor­der­lich ihr Ver­hal­ten in der so genann­ten Pseu­do-Klas­se neu zu defi­nie­ren. In Java­FX 1.7 wird das so gemacht:

protected static class RangeButton extends Button {
        public RangeButton(){
            getStyleClass().add("MyCell");
        }
 
 
        private BooleanProperty inRange = new BooleanPropertyBase() {
 
 
            @Override
            protected void invalidated() {
                impl_pseudoClassStateChanged("inRange");
            }
 
 
            @Override
            public Object getBean() {
                return RangeButton.this;
            }
 
 
            @Override
            public String getName() {
                return "inRange";
            }
        };
 
 
        public boolean isInRange() {
            return inRange.get();
        }
 
 
        public void setInRange(boolean value) {
            inRange.set(value);
        }
 
 
        private static final long IN_RANGE_PSEUDOCLASS_STATE = StyleManager.getInstance().getPseudoclassMask("inRange");
 
 
        @Override
        public long impl_getPseudoClassState() {
            long mask = super.impl_getPseudoClassState();
            if (isInRange()) mask |= IN_RANGE_PSEUDOCLASS_STATE;
            return mask;
        }
    }

Wie wir sehen, sind alle Metho­den «…Pseu­do­Class…» — depre­ca­ted.
Jetzt nut­zen wir statt But­ton unse­ren Ran­ge­But­ton. Und die Metho­de selec­t­Cell sieht nun so aus:

private void selectCell(int col, int row, final boolean select){
            final Node node = getNodeFromGridPane(col, row);
            ((RangeButton)node).setInRange(select);
        }

D.h. die Ände­rung der Feld­ei­gen­schaft «InRange» führt dazu, dass der Style-Trig­ger greift und die Far­be der aus­ge­wähl­ten Zel­len sich ent­spre­chend ändert.
Das ist genau das, was wir brau­chen!
In Java­FX 1.7 funk­tio­niert das. In Java­FX 1.8 ist das lei­der ver­bo­ten. Der Code ist nicht mehr kom­pi­lier­bar sobald JVM 1.8 hin­zu­ge­schal­tet wird.
Was bie­tet uns dann die neue Ver­si­on an der Stel­le?
Wie bereits erwar­tet, wur­den die depre­ca­ted Metho­den ent­fernt und die Archi­tek­tur ver­ein­facht. Jetzt reicht es daher aus, wenn wir fol­gen­des tun:

protected static class RangeButton extends Button {        protected final PseudoClass pcInRange = PseudoClass.getPseudoClass("inRange");
 
 
        public RangeButton(){
            getStyleClass().add("MyCell");
        }
 
 
        protected final BooleanProperty inRange = new BooleanPropertyBase() {
 
 
            @Override
            protected void invalidated() {
                pseudoClassStateChanged(pcInRange, getValue());
            }
 
 
            @Override
            public Object getBean() {
                return RangeButton.this;
            }
 
 
            @Override
            public String getName() {
                return "inRange";
            }
        };
 
 
        public boolean isInRange() {
            return inRange.get();
        }
 
 
        public void setInRange(boolean value) {
            inRange.set(value);
        }
    }

Alles funk­tio­niert wie bis­her.
Man kann den Code noch etwas ver­ein­fa­chen…:

protected static class RangeButton extends Button {
        protected final BooleanProperty inRange;
        public RangeButton(){
            getStyleClass().add("MyCell");
            final PseudoClass pcInRange = PseudoClass.getPseudoClass("inRange");
            inRange = new SimpleBooleanProperty();
            inRange.addListener(new ChangeListener() {
                @Override
                public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                    pseudoClassStateChanged(pcInRange, newValue);
                }
            });
        }

…und die Metho­de ent­spre­chend ändern:

private void selectCell(int col, int row, final boolean select){
            final Node node = getNodeFromGridPane(col, row);
            ((RangeButton)node).inRange.setValue(select);
        }

Zusam­men­fas­sung: Mit Hil­fe der beschrie­be­nen Lösung auf Java­FX ist es mög­lich, spe­zi­el­le user­be­zo­ge­ne Eigen­schaf­ten der Kom­po­nen­ten zu erstel­len und die­se mit den Styles zu ver­knüp­fen,. Das ist beson­ders bequem, wenn die Anfor­de­rung besteht, die Busi­ness­lo­gik kom­plett von der GUI zu abs­tra­hie­ren.

Wir neh­men an der IT-Mes­se DIS­CO­VER ICT im rus­si­schen Eka­te­rin­burg teil. Die Teil­nah­me an einer Mes­se ist eine gute Gele­gen­heit neue Kon­tak­te, ggf. Ver­trä­ge zu gewin­nen.

Mehr Infor­ma­ti­on über die Mes­se kön­nen Sie hier DIS­CO­VER ICT fin­den.