How do I add a JavaFX Pane to a TornadoFX vbox?
All I get is a blank window.
class TradingButtons : View() {
override val root = vbox {
ChartTest()
}
}
class ChartTest(vararg children: Node?) : Pane(*children) {
init {
val xAxis = CategoryAxis()
val yAxis = NumberAxis(1.0, 21.0, 0.1)
val lineChart = LineChart(xAxis, yAxis)
this.children.add(lineChart)
}
}
As per the documentation:
However, to actually show something on screen we want to populate this VBox acting as the root control. Using the initializer block, let's add a JavaFX Button and a Label.
Also take note of the tornadofx.* import. This is important and should be present in all your TornadoFX related files. The reason this is important is that some of the functionality of the framework isn't discoverable by the IDE without the import. This import enabled some advanced extension functions that you really don't want to live without :)
import tornadofx.*
class MyView: View() {
override val root = vbox {
button("Press me")
label("Waiting")
}
}
TornadoFX provides a builder syntax that will streamline your UI code. Instead of creating UI elements and manually adding them to the parent element's children list, the builders allow us to express the UI as a hierarchical structure which enables you to visualize the resulting UI very easily. Note that all builders are written in lowercase, so as to not confuse them with manual instantiation of the UI element classes.
I hope, this may helps.
Related
I'm creating a UI for a project in javafx. I'm using CSS for the styles, FXML for the structure, and also .java controllers. I'm trying to make my node(s) (i.e. button, borderpane) layout bind to the scene size, so that the window can be resized and the layout stay the same. Normally if the node was defined in a jar file I would use:
button.layoutYProperty().bind((scene.heightProperty().divide(2)));
or something similar, but my nodes are defined within the FXML file.
How can I access the nodes from outside of the FXML file so that I can define the layout or how can I define the layout within the FXML file.
(I am not using and do not intend to use scene builder).
First, you should never need to do things like this. If you use layout panes you should always be able to choose a combination of layout panes and settings that automatically position the nodes as you want them. The solutions outlined below should never really be needed.
In the controller, you typically don't have direct access to the scene, so you need to listen for when the sceneProperty() is initialized on the node and act accordingly:
public class Controller {
#FXML
private Button button ;
public void initialize() {
button.sceneProperty().addListener((obs, oldScene, newScene) -> {
button.layoutYProperty().unbind();
if (newScene != null) {
button.layoutYProperty().bind(newScene.heightProperty().divide(2));
}
});
// ...
}
}
But again, the correct approach here is to choose appropriate layout panes to achieve what you want.
I was inspired by article Decouple the View and its behavior to create a testable UI and wanna rework my JavaFX application with MVVM pattern.
(source: s-msft.com)
.
Ideally View Model should be View-independent and be testable as usual Java class. Also MVVM uses Command conseption to change View Model from View. So Command implementation is a part of View Model implementation and may be simply tested.
The questions is how Command should be implemented if it's result is a View changing? E.g. in View_1 I have a button after pressing on it a new View (e.g. View_2) should be created and shown. Should such Command be a part of a View and not be testable at all?
I think command could be testable. I'm not sure how exactly are you going to switch views, but AFAIK in JavaFX there is something like a Scene and Stage which is parent for scenes, is that right?
So in your command you pass the Stage as a dependency and on execution you set it a new scene.
And you can easily test it in unit tests - you can mock Stage and check if command passes correct scene to it.
I don't remember exact details of JavaFX but I would implement it like this:
class SwitchSceneCommand {
protected Stage stage;
protected Scene originalScene; // you can keep original scene if you want to have some undo-redo functionality
protected Scene newScene;
public SwitchSceneCommand(Stage stage, Scene originalScene, Scene newScene)
{
this.stage = stage;
this.originalScene = originalScene;
this.newScene = newScene;
}
public void execute()
{
this.stage.setScene(this.newScene);
}
}
I've studied all popular GUI patterns - MVP,MVC,MVVM and finally I decided to implement MVP (Supervising Controller). So I have the following OBJECTS(!). Stage<-View<->Model. It's important Stage!=View, it is another object. Between view and model data binding. Besides I have a presenter(controller) which handles all events and works with view and model, so View<-ViewInterface<-Controller->Model.
The problem is now how to get references to labels, textAreas etc in view. Javafx allows to use #FXML annotation to inject these components to controller. However, using MVP I need these components in View, as all logic for view is in View and I don't need them in controller. The only solution I know is:
public class MyView{
private Button button;
public MyView(){
...
button=(Button) root.lookup("#myButton");
}
}
That is to get references by their ID. However I don't like it. Or I do something wrong or I understand something wrong but I think a better solution exist. Please, help me to find it.
JavaFX has been designed to work with the MVC pattern. Hence it is much easier to use MVC than MVP. In MVP Presenter is responsible for formatting the data to be displayed. In JavaFX, it is done automatically by View. Here's a quick overview of JavaFX MVC:
Model - the domain data / data structure that you work with in your application (e.g. Person, Employer, Coursework, etc)
View - the UI definition of the application and its Model. The preferred way of creating a view is via an FXML file, which is essentially the View in JavaFX MVC.
Controller - the bridge between Model and View. The code is typically isolated in XController class (where X is the name of the FXML View). The instance of Controller is automatically injected by FXMLLoader or can be done manually in case you require a custom Controller. The Controller class will have access to UI (View) elements in order to be able to manipulate different properties and also the Model, so that it can perform operations based on the UI (View) input.
To sum up, in JavaFX you don't need to have class View, the View definition should be entirely in the FXML file. All UI elements should be injected with #FXML into your Controller class. If you absolutely have to use MVP, then AWT/Swing or MVP4j - http://www.findbestopensource.com/product/mvp4j might be a better option.
For more detailed explanation please have a look at the official Oracle tutorial for JavaFX: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-overview.htm
If you require help building UI using FXML: http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html
This tutorial covers basics of MVC in JavaFX and how each component communicates with others: http://code.makery.ch/library/javafx-8-tutorial/part1/
As an Android developer, I always use MVP pattern in my applications. MVC compared to MVP seems so old to me, so when I started working on a new Java app, I felt a little bit lost.
Here there is my solution:
Initial steps
In fxml files create the UI, without specifying a controller, because you don't need one.
Create the Java interfaces (IView, IPresenter and so on..)
Implement the IPresenter interface in the Presenter class, as you would do normally (do http requests, query a DB..)
Now the interesting part:
Adapting your view to MVP pattern
Let's see some code:
Create your GUI (for example a Main GUI) and implement your View interface
public class MainGUI extends Application implements MainContract.View {
public static void main(String... args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
//here we will load fxml or create the ui programmatically
}
//method from view interface
#Override
public void onServerResponse(String message) throws IOException {
//update the view
}
Now the last step:
Communicating with the presenter
To do this, we first create an istance of our presenter:
private MainContract.Presenter presenter;
public MainGUI() {
presenter = new MainPresenter(this);
}
this is, of course, the MainContract.View implemented in the MainGUI class
Now we have to get a reference to the view components
private ComboBox<Double> mySimpleList;
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("layout_main.fxml"));
Parent root = loader.load();
mySimpleList= (ComboBox<Double>) loader.getNamespace().get("mysimplelist_id");
...
primaryStage.setScene(new Scene(root, -1, -1));
primaryStage.show();
I prefer using fxml files instead of creating the ui by code, but the logic behind is identical.
Set the items
...
mySimpleList.setItems(ValuesFactory.getMyValues());
And the listener
...
mySimpleList.valueProperty().addListener(simpleListListener);
What is simpleListListener?
A simple ChangeListener, where we finally call a presenter method
simpleListListener = (ChangeListener<Double>)
(observable, oldValue, newValue) -> presenter.doTheLogic(newValue);
This is an easy scenario, but in principle this is how we can use MVP Pattern with JavaFX. I also understand that it isn't the definitive solution, so I hope that one day there will be more docs where I can learn more about this argument!
Let me know if I wasn't clear in some part of the code
I have a JavaFX control that is basically an amalgamation of several other JavaFX controls.
I want it such that the .jar file can be imported into Scene Builder so that it can be used like any other control. The closest analogy I can think of is when you make a custom control in C# and use it several times throughout several projects.
When I try to import the FXML file, it doesn't work. The control isn't treated as a single entity, and instead is basically just all of it's parts strung out in the FXML file.
What do I need to do with the FXML file, or the controller.java file so that the Scene Builder will be able to import the .jar, see the control(s), and allow me to import and use each custom control as a single entity? I've looked several places and even asked on Stack Overflow once before (though the answer I got was not the one for which I was looking, and have received no responses since), but nothing I've seen comes close to handling my issue.
The closest I've come has to do with this line in the FXML file:
<?scenebuilder-classpath-element /path/to/something?>
but I don't know what goes in /path/to/something
I know I can, in the initialization, simply add the control to the scene, but that is sub-optimal and something which I am desperately trying to avoid.
I was finally able to resolve the issue. After much trial and error and following the sample code that came from here, I discovered my problem was that I needed 2 classes for each FXML control group.
One to be the actual controller of the group, and another to be the object that would house the controller. I followed the code in the Unlock example and it was a godsend for helping me.
Basically it comes down to two files:
The object (which extends the type of the root node, in my case):
public class <InsertObjectClassNameHere> extends <RootContainerTypeHere>{
}
After that you need the controller class. This is with what I am most familiar, however I was still doing it wrong. This is what needs to implement initializable:
public class <InsertControllerClassNameHere> implements Initializable{
}
So for me the Object class looks like this:
public class DGCSDefiner extends GridPane {
private final DGCSDefinerController Controller;
public DGCSDefiner(){
this.Controller = this.Load();
}
private DGCSDefinerController Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("DGCSDefiner.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch(IOException ex){
throw new IllegalStateException(ex);
}
final DGCSDefinerController cntrlr = loader.getController();
assert cntrlr != null;
return cntrlr;
}
/**
* Get the settings defined by the controller.
* #return controller defined settings.
*/
public ColorSettings getColorSettings(){
return this.Controller.getColorSettings();
}
/**
* Set the controllers color settings.
* #param CS Defined color settings.
*/
public void setColorSettings(ColorSettings CS){
this.Controller.setColorSettings(CS);
}
}
and then there is the actual Controller class.
So for a straight-forward answer,
you need to have a class that will be loading your controller, and you need to pass down from your controller to that class that with which you will be working (Or, you can simply keep the controller public and access it directly).
I'm starting working with JavaFX and wish to use the new tree view (as you can use multiple icons to represent your data - which is what I wish to take advantage of).
I have created a basic form/scene that has a tree view and one button on it. When this button is pressed I wish to populate the treeview.
Now, all the examples ive looked at are where the form/scene is generated in code and the treeview is bound to that control....how do I have a pre designed form with Scene builder and populate it from external code?
You could use the following code in a controller class. Inside the FXML file you will need to set the FXID to selectionTreeView. Tested in JDK 8u5 and it worked.
#FXML
TreeView selectionTreeView;
#FXML
private void handleButtonAction(ActionEvent event) {
createTree();
}
public void createTree(String... rootItems) {
//create root
TreeItem<String> root = new TreeItem<>("Root");
//root.setExpanded(true);
//create child
TreeItem<String> itemChild = new TreeItem<>("Child");
itemChild.setExpanded(false);
//root is the parent of itemChild
root.getChildren().add(itemChild);
selectionTreeView.setRoot(root);
}
Set the class name (including package) on the root node of your control in scene builder. If you click on, then go to the code tab on the right it is the top field.
Now set an ID on the TreeView in your control.
Now in the controller object add a TreeView field, the variable name should be the same as what you set the TreeView ID as in scene builder. Annotate with field with #FXML.
Now when the FXML is loaded, the controller is created and the TreeView field is set.