Zoeken…


Syntaxis

Voorbeeld FXML

Een eenvoudig FXML-document met een AnchorPane met een knop en een AnchorPane :

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" 
        fx:controller="com.example.FXMLDocumentController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>

Dit voorbeeld FXML-bestand is gekoppeld aan een controllerklasse. De associatie tussen de FXML en de controllerklasse wordt in dit geval gemaakt door de klassenaam op te geven als de waarde van het kenmerk fx:controller in het root-element van de FXML: fx:controller="com.example.FXMLDocumentController" . Met de controllerklasse kan Java-code worden uitgevoerd als reactie op gebruikersacties op de UI-elementen die zijn gedefinieerd in het FXML-bestand:

package com.example ;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class FXMLDocumentController {
    
    @FXML
    private Label label;
    
    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }
    
    @Override
    public void initialize(URL url, ResourceBundle resources) {
        // Initialization code can go here. 
        // The parameters url and resources can be omitted if they are not needed
    }    
    
}

Een FXMLLoader kan worden gebruikt om het FXML-bestand te laden:

public class MyApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
        Parent root = loader.load();
        
        Scene scene = new Scene(root);
        
        stage.setScene(scene);
        stage.show();
    }

}

De load methode voert verschillende acties, en is het nuttig om de volgorde waarin ze zich voordoen te begrijpen. In dit eenvoudige voorbeeld:

  1. De FXMLLoader leest en parseert het FXML-bestand. Het maakt objecten die overeenkomen met de elementen die in het bestand zijn gedefinieerd, en noteert eventuele fx:id attributen die erop zijn gedefinieerd.

  2. Aangezien het root-element van het FXML-bestand een kenmerk fx:controller gedefinieerd, maakt de FXMLLoader een nieuwe instantie van de klasse die hij opgeeft. Standaard gebeurt dit door de constructor zonder argument voor de opgegeven klasse aan te roepen.

  3. Alle elementen met gedefinieerde fx:id kenmerken die velden in de controller hebben met overeenkomende veldnamen, en die ofwel public (niet aanbevolen) of geannoteerd @FXML (aanbevolen) zijn, worden "geïnjecteerd" in die overeenkomstige velden. In dit voorbeeld is er dus een Label in het FXML-bestand met fx:id="label" en een veld in de controller gedefinieerd als

    @FXML
    private Label label ;
    

    het label veld wordt geïnitialiseerd met de Label bijvoorbeeld door de FXMLLoader .

  4. Gebeurtenishandlers worden geregistreerd bij alle elementen in het FXML-bestand met de onXXX="#..." gedefinieerd. Deze gebeurtenishandlers roepen de opgegeven methode in de controllerklasse aan. In dit voorbeeld heeft de Button onAction="#handleButtonAction" en definieert de controller een methode

    @FXML
    private void handleButtonAction(ActionEvent event) { ... }
    

    wanneer een actie op de knop wordt geactiveerd (bijvoorbeeld wanneer de gebruiker erop drukt), wordt deze methode aangeroepen. De methode moet een void ActionEvent hebben en kan een parameter definiëren die overeenkomt met het gebeurtenistype ( ActionEvent in dit voorbeeld) of kan geen parameters definiëren.

  5. Als ten slotte de controllerklasse een initialize definieert, wordt deze methode aangeroepen. Merk op dat dit gebeurt nadat de @FXML velden zijn geïnjecteerd, zodat ze veilig toegankelijk zijn via deze methode en worden geïnitialiseerd met de instanties die overeenkomen met de elementen in het FXML-bestand. De methode initialize() kan geen parameters aannemen, of een URL en een ResourceBundle . In het laatste geval worden deze parameters ingevuld door de URL die de locatie van het FXML-bestand vertegenwoordigt, en elke ResourceBundle die op de FXMLLoader via loader.setResources(...) . Beide kunnen null als ze niet waren ingesteld.

Geneste controllers

Het is niet nodig om de hele gebruikersinterface in een enkele FXML te maken met behulp van een enkele controller.

De tag <fx:include> kan worden gebruikt om het ene fxml-bestand in het andere op te nemen. De controller van de meegeleverde fxml kan net als elk ander object dat door de FXMLLoader gemaakt, in de controller van het bijbehorende bestand worden geïnjecteerd.

Dit wordt gedaan door het kenmerk fx:id toe te voegen aan het element <fx:include> . Op deze manier wordt de controller van de meegeleverde fxml in het veld geïnjecteerd met de naam <fx:id value>Controller .

Voorbeelden:

fx: id waarde veldnaam voor injectie
foo fooController
answer42 answer42Controller
xyz xYzController

Voorbeeld fxmls

Teller

Dit is een fxml met een StackPane met een Text knooppunt. Met de controller voor dit fxml-bestand kan de huidige tellerwaarde worden opgehaald en de teller worden verhoogd:

counter.fxml

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

<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>

<StackPane prefHeight="200" prefWidth="200" xmlns:fx="http://javafx.com/fxml/1" fx:controller="counter.CounterController">
    <children>
        <Text fx:id="counter" />
    </children>
</StackPane>

CounterController

package counter;

import javafx.fxml.FXML;
import javafx.scene.text.Text;

public class CounterController {
    @FXML
    private Text counter;

    private int value = 0;
    
    public void initialize() {
        counter.setText(Integer.toString(value));
    }
    
    public void increment() {
        value++;
        counter.setText(Integer.toString(value));
    }
    
    public int getValue() {
        return value;
    }
    
}

Inclusief fxml

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane prefHeight="500" prefWidth="500" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="counter.OuterController">
    <left>
        <Button BorderPane.alignment="CENTER" text="increment" onAction="#increment" />
    </left>
    <center>
        <!-- content from counter.fxml included here -->
        <fx:include fx:id="count" source="counter.fxml" />
    </center>
</BorderPane>

OuterController

De controller van de meegeleverde fxml wordt in deze controller geïnjecteerd. Hier wordt de handler voor de gebeurtenis onAction voor de Button gebruikt om de teller te verhogen.

package counter;

import javafx.fxml.FXML;

public class OuterController {

    // controller of counter.fxml injected here
    @FXML
    private CounterController countController;

    public void initialize() {
        // controller available in initialize method
        System.out.println("Current value: " + countController.getValue());
    }

    @FXML
    private void increment() {
        countController.increment();
    }

}

De fxmls kunnen op deze manier worden geladen, ervan uitgaande dat de code wordt aangeroepen vanuit een klasse in hetzelfde pakket als outer.fxml :

Parent parent = FXMLLoader.load(getClass().getResource("outer.fxml"));

Definieer blokken en

Soms moet een element buiten de gebruikelijke objectstructuur in de fxml worden gemaakt.

Dit is waar Define Blocks in het spel komen:

Inhoud in een element <fx:define> wordt niet toegevoegd aan het object dat voor het bovenliggende element is gemaakt.

Elk onderliggend element van het <fx:define> heeft een fx:id attribuut nodig.

Naar objecten die op deze manier zijn gemaakt, kan later worden verwezen met het element <fx:reference> of met expressiebinding.

Het element <fx:reference> kan worden gebruikt om te verwijzen naar elk element met een kenmerk fx:id dat wordt verwerkt voordat het element <fx:reference> wordt afgehandeld met dezelfde waarde als het kenmerk fx:id van het element waarnaar wordt verwezen in de source attribuut van de <fx:reference> element.

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

<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>


<VBox xmlns:fx="http://javafx.com/fxml/1" prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8">
    <children>
        <fx:define>
            <String fx:value="My radio group" fx:id="text" />
        </fx:define>
        <Text>
            <text>
                <!-- reference text defined above using fx:reference -->
                <fx:reference source="text"/>
            </text>
        </Text>
        <RadioButton text="Radio 1">
            <toggleGroup>
                <ToggleGroup fx:id="group" />
            </toggleGroup>
        </RadioButton>
        <RadioButton text="Radio 2">
            <toggleGroup>
                <!-- reference ToggleGroup created for last RadioButton -->
                <fx:reference source="group"/>
            </toggleGroup>
        </RadioButton>
        <RadioButton text="Radio 3" toggleGroup="$group" />
        
        <!-- reference text defined above using expression binding -->
        <Text text="$text" />
    </children>
</VBox>

Gegevens doorgeven aan FXML - toegang tot bestaande controller

Probleem: sommige gegevens moeten worden doorgegeven aan een scène die is geladen vanuit een fxml.

Oplossing

Geef een controller op met behulp van het kenmerk fx:controller en haal de controllerinstantie die tijdens het laadproces is gemaakt van de FXMLLoader instantie die wordt gebruikt om de fxml te laden.

Voeg methoden toe om de gegevens door te geven aan de controller-instantie en behandel de gegevens in die methoden.

FXML

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

<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="valuepassing.TestController">
    <children>
        <Text fx:id="target" />
    </children>
</VBox>

controleur

package valuepassing;

import javafx.fxml.FXML;
import javafx.scene.text.Text;

public class TestController {

    @FXML
    private Text target;

    public void setData(String data) {
        target.setText(data);
    }

}

Code die wordt gebruikt voor het laden van de fxml

String data = "Hello World!";

FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));
Parent root = loader.load();
TestController controller = loader.<TestController>getController();
controller.setData(data);

Gegevens doorgeven aan FXML - Het controller-exemplaar opgeven

Probleem: sommige gegevens moeten worden doorgegeven aan een scène die is geladen vanuit een fxml.

Oplossing

Stel de controller in met behulp van de FXMLLoader instantie die later wordt gebruikt om de fxml te laden.

Zorg ervoor dat de controller de relevante gegevens bevat voordat u de fxml laadt.

Opmerking: in dit geval mag het fxml-bestand niet het kenmerk fx:controller bevatten.

FXML

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

<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml/1">
    <children>
        <Text fx:id="target" />
    </children>
</VBox>

controleur

import javafx.fxml.FXML;
import javafx.scene.text.Text;

public class TestController {

    private final String data;

    public TestController(String data) {
        this.data = data;
    }
    
    @FXML
    private Text target;
    
    public void initialize() {
        // handle data once the fields are injected
        target.setText(data);
    }

}

Code die wordt gebruikt voor het laden van de fxml

String data = "Hello World!";

FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));

TestController controller = new TestController(data);
loader.setController(controller);

Parent root = loader.load();

Parameters doorgeven aan FXML - met behulp van een controllerFactory

Probleem: sommige gegevens moeten worden doorgegeven aan een scène die is geladen vanuit een fxml.

Oplossing

Geef een controllerfabriek op die verantwoordelijk is voor het maken van de controllers. Geef de gegevens door aan de controllerinstantie die door de fabriek is gemaakt.

FXML

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

<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="valuepassing.TestController">
    <children>
        <Text fx:id="target" />
    </children>
</VBox>

controleur

package valuepassing;

import javafx.fxml.FXML;
import javafx.scene.text.Text;

public class TestController {

    private final String data;

    public TestController(String data) {
        this.data = data;
    }
    
    @FXML
    private Text target;
    
    public void initialize() {
        // handle data once the fields are injected
        target.setText(data);
    }

}

Code die wordt gebruikt voor het laden van de fxml

String data = "Hallo wereld!";

Map<Class, Callable<?>> creators = new HashMap<>();
creators.put(TestController.class, new Callable<TestController>() {

    @Override
    public TestController call() throws Exception {
        return new TestController(data);
    }

});

FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));

loader.setControllerFactory(new Callback<Class<?>, Object>() {

    @Override
    public Object call(Class<?> param) {
        Callable<?> callable = creators.get(param);
        if (callable == null) {
            try {
                // default handling: use no-arg constructor
                return param.newInstance();
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new IllegalStateException(ex);
            }
        } else {
            try {
                return callable.call();
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        }
    }
});

Parent root = loader.load();

Dit lijkt misschien ingewikkeld, maar het kan handig zijn als de fxml moet kunnen beslissen welke controller-klasse het nodig heeft.

Instance van exemplaren in FXML

De volgende klasse wordt gebruikt om aan te tonen hoe instanties van klassen kunnen worden gemaakt:

JavaFX 8

De annotatie in Person(@NamedArg("name") String name) moet worden verwijderd, omdat de annotatie @NamedArg niet beschikbaar is.

package fxml.sample;

import javafx.beans.NamedArg;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Person {

    public static final Person JOHN = new Person("John");

    public Person() {
        System.out.println("Person()");
    }
    
    public Person(@NamedArg("name") String name) {
        System.out.println("Person(String)");
        this.name.set(name);
    }
    
    public Person(Person person) {
        System.out.println("Person(Person)");
        this.name.set(person.getName());
    }

    private final StringProperty name = new SimpleStringProperty();

    public final String getName() {
        System.out.println("getter");
        return this.name.get();
    }

    public final void setName(String value) {
        System.out.println("setter");
        this.name.set(value);
    }

    public final StringProperty nameProperty() {
        System.out.println("property getter");
        return this.name;
    }
    
    public static Person valueOf(String value) {
        System.out.println("valueOf");
        return new Person(value);
    }
    
    public static Person createPerson() {
        System.out.println("createPerson");
        return new Person();
    }
    
}

Stel dat de klasse Person al is geïnitialiseerd voordat de fxml wordt geladen.

Een opmerking over invoer

In het volgende fxml-voorbeeld wordt de importsectie weggelaten. De fxml zou echter moeten beginnen met

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

gevolgd door een importsectie die alle klassen importeert die in het fxml-bestand worden gebruikt. Die invoer is vergelijkbaar met niet-statische invoer, maar wordt toegevoegd als verwerkingsinstructies. Zelfs klassen uit het pakket java.lang moeten worden geïmporteerd.

In dit geval moeten de volgende invoer worden toegevoegd:

<?import java.lang.*?>
<?import fxml.sample.Person?>

JavaFX 8

@NamedArg geannoteerde constructor

Als er een constructor is waarbij elke parameter is geannoteerd met @NamedArg en alle waarden van de @NamedArg annotaties aanwezig zijn in de fxml, wordt de constructor met die parameters gebruikt.

<Person name="John"/>
<Person xmlns:fx="http://javafx.com/fxml">
    <name>
        <String fx:value="John"/>
    </name>
</Person>

Beide resulteren in de volgende console-uitvoer, indien geladen:

Person(String)

Geen args constructor

Als er geen geschikte geannoteerde @NamedArg constructor beschikbaar is, wordt de constructor gebruikt die geen parameters gebruikt.

Verwijder de annotatie @NamedArg van de constructor en probeer te laden.

<Person name="John"/>

Dit gebruikt de constructor zonder parameters.

Output:

Person()
setter

fx:value

Het kenmerk fx:value kan worden gebruikt om de waarde door te geven aan een static valueOf methode die een parameter String en de te gebruiken instantie teruggeeft.

Voorbeeld

<Person xmlns:fx="http://javafx.com/fxml" fx:value="John"/>

Output:

valueOf
Person(String)

fx:factory

Met het kenmerk fx:factory kunnen objecten worden gemaakt met willekeurige static methoden die geen parameters aannemen.

Voorbeeld

<Person xmlns:fx="http://javafx.com/fxml" fx:factory="createPerson">
    <name>
        <String fx:value="John"/>
    </name>
</Person>

Output:

createPerson
Person()
setter

<fx:copy>

Met behulp van fx:copy een copy constructor worden aangeroepen. Onder vermelding van de fx:id van een andere De source attribuut van de tag wordt de kopie constructeur met dat object als parameter te roepen.

Voorbeeld:

<ArrayList xmlns:fx="http://javafx.com/fxml">
    <Person fx:id="p1" fx:constant="JOHN"/>
    <fx:copy source="p1"/>
</ArrayList>

uitgang

Person(Person)
getter

fx:constant

fx:constant maakt het mogelijk een waarde te krijgen uit een static final veld.

Voorbeeld

<Person xmlns:fx="http://javafx.com/fxml" fx:constant="JOHN"/>

zal geen uitvoer produceren, omdat dit alleen verwijst naar JOHN die is gemaakt bij het initialiseren van de klasse.

Eigenschappen instellen

Er zijn meerdere manieren om gegevens toe te voegen aan een object in fxml:

<property> tag

Een tag met de naam van een eigenschap kan worden toegevoegd als onderliggend element van een element dat wordt gebruikt voor het maken van een instantie. Het kind van deze tag wordt aan de eigenschap toegewezen met behulp van de setter of toegevoegd aan de inhoud van de eigenschap (alleen-lezen lijst / kaarteigenschappen).

Standaard eigenschap

Een klasse kan worden geannoteerd met de annotatie @DefaultProperty . In dit geval kunnen elementen rechtstreeks worden toegevoegd als onderliggend element zonder een element met de naam van de eigenschap te gebruiken.

property="value" attribuut

Eigenschappen kunnen worden toegewezen met behulp van de eigenschapsnaam als kenmerknaam en de waarde als kenmerkwaarde. Dit heeft hetzelfde effect als het toevoegen van het volgende element als onderliggend element van de tag:

<property>
    <String fx:value="value" />
</property>

statische setters

Eigenschappen kunnen ook worden ingesteld met behulp van static setters. Dit zijn static methoden met de naam setProperty die het element als eerste parameter nemen en de waarde die als tweede parameter moet worden ingesteld. Die methoden kunnen zich in elke klasse bevinden en kunnen worden gebruikt met ContainingClass.property plaats van de gebruikelijke eigenschapsnaam.

Opmerking: Momenteel lijkt het nodig om een overeenkomstige statische getter-methode te hebben (dwz een statische methode genaamd getProperty die het element als parameter in dezelfde klasse als de statische setter neemt) om dit te laten werken, tenzij het waardetype String .

Type dwang

Het volgende mechanisme wordt gebruikt om een object van de juiste klasse te krijgen tijdens toewijzingen, bijvoorbeeld voor het parametertype van een setter-methode.

Als de klassen toewijsbaar zijn, wordt de waarde zelf gebruikt.

Anders wordt de waarde als volgt geconverteerd

Type doel gebruikte waarde (bronwaarde s )
Boolean , boolean Boolean.valueOf(s)
char , Character s.toString.charAt(0)
ander primitief type of wikkeltype geschikte methode voor het valueOf(s.toString()) , in het geval dat de s een Number , anders de waarde van valueOf(s.toString()) voor het wrapper-type
BigInteger BigInteger.valueOf(s.longValue()) is s is een Number , new BigInteger(s.toString()) anders
BigDecimal BigDecimal.valueOf(s.doubleValue()) is s is een Number , new BigDecimal(s.toString()) anders
Aantal Double.valueOf(s.toString()) als s.toString() bevat . , Long.valueOf(s.toString()) anders
Class Class.forName(s.toString()) aangeroepen met de context ClassLoader van de huidige thread zonder de klasse te initialiseren
enum Het resultaat van de methode valueOf , bovendien geconverteerd naar een String hoofdletters gescheiden door _ ingevoegd voor elke hoofdletter, als s een String die begint met een kleine letter
anders de waarde die wordt geretourneerd door een static valueOf methode in het targetType, die een parameter heeft die overeenkomt met het type s of een superklasse van dat type

Opmerking: dit gedrag is niet goed gedocumenteerd en kan aan verandering onderhevig zijn.

Voorbeeld

public enum Location {
    WASHINGTON_DC,
    LONDON;
}
package fxml.sample;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.DefaultProperty;

@DefaultProperty("items")
public class Sample {
    
    private Location loaction;

    public Location getLoaction() {
        return loaction;
    }

    public void setLoaction(Location loaction) {
        this.loaction = loaction;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
    
    int number;

    private final List<Object> items = new ArrayList<>();

    public List<Object> getItems() {
        return items;
    }
    
    private final Map<String, Object> map = new HashMap<>();

    public Map<String, Object> getMap() {
        return map;
    }
    
    private BigInteger serialNumber;

    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    public void setSerialNumber(BigInteger serialNumber) {
        this.serialNumber = serialNumber;
    }

    @Override
    public String toString() {
        return "Sample{" + "loaction=" + loaction + ", number=" + number + ", items=" + items + ", map=" + map + ", serialNumber=" + serialNumber + '}';
    }
    
}
package fxml.sample;

public class Container {

    public static int getNumber(Sample sample) {
        return sample.number;
    }

    public static void setNumber(Sample sample, int number) {
        sample.number = number;
    }

    private final String value;

    private Container(String value) {
        this.value = value;
    }

    public static Container valueOf(String s) {
        return new Container(s);
    }

    @Override
    public String toString() {
        return "42" + value;
    }

}

Het resultaat afdrukken van het laden van de onderstaande fxml bestandsopbrengsten

Sample{loaction=WASHINGTON_DC, number=5, items=[42a, 42b, 42c, 42d, 42e, 42f], map={answer=42, g=9.81, hello=42A, sample=Sample{loaction=null, number=33, items=[], map={}, serialNumber=null}}, serialNumber=4299}
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import fxml.sample.*?>

<Sample xmlns:fx="http://javafx.com/fxml/1" Container.number="5" loaction="washingtonDc">
    
    <!-- set serialNumber property (type coercion) -->
    <serialNumber>
        <Container fx:value="99"/>
    </serialNumber>
    
    <!-- Add elements to default property-->
    <Container fx:value="a"/>
    <Container fx:value="b"/>
    <Container fx:value="c"/>
    <Container fx:value="d"/>
    <Container fx:value="e"/>
    <Container fx:value="f"/>
    
    <!-- fill readonly map property -->
    <map g="9.81">
        <hello>
            <Container fx:value="A"/>
        </hello>
        <answer>
            <Container fx:value=""/>
        </answer>
        <sample>
            <Sample>
                <!-- static setter-->
                <Container.number>
                    <Integer fx:value="33" />
                </Container.number>
            </Sample>
        </sample>
    </map>
</Sample>


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow