This question already has answers here:
Passing Parameters JavaFX FXML
(10 answers)
Closed 8 years ago.
i am new to javafx and i want to transfer variable values from one controller to another and i have no idea how to do this. so please help me.
for example:
i want to display username from first login window to second dashboard window so what should i do to save userid in one variable and send it to second window and display there in a label.
code test:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
/**
* FXML Controller class
*
* #author wabcon
*/
public class AdmissionController implements Initializable {
int userid=0;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
userid=10001;
}
}
how could i send this userid to next window controller.
please help me.
Thank you.
I am assuming you are doing this for a custom component.
So, you create a class for your custom component and set that class as the controller:
public class CustomControl extends AnchorPane implements Initializable {
String customId;
public CustomControl() {
//if you want to set a FXML
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/res/customControl.fxml"));
//Defines this class as the controller
fxmlLoader.setRoot(this);
//this.getStylesheets().add("/res/style.css"); <- if you want to set a css
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public String getCustomId() {
return customId;
}
public void setCustomId(String customId) {
return this.customId = customId;
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
//Initializes the controller
}
}
On your MainController:
CustomControl c = new CustomControl();
c.setCustomId("StackOverflow");
Related
In many samples it is shown how to extend Application method to have JavaFX app composed and ran.
But what if I don't want to? What if I want to configure app dynamically from my code? Example is below:
import javafx.application.Application;
import javafx.stage.Stage;
public class HollowTry {
public static class HollowApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
}
}
public static void main(String[] args) {
Application.launch(HollowApplication.class, args);
// now I want to set title, scene etc... how?
}
}
Please don't dispute on why I need it.
UPDATE
Okay, launch() is never terminated, I didn't check this. Anyway, I need to have a way to build application programmatically, without any hardcoding.
UPDATE 2
I was wishing con build application from Spring and I found the following solution for now.
JavaFX wrapper class
It wraps context initialization into FX thread and captures config classes to be accessible from start():
public class SpringJavaFX extends Application {
private static Class<?>[] annotatedClasses;
#Override
public void start(Stage primaryStage) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(annotatedClasses);
String title = (String) context.getBean("primaryStageTitle");
primaryStage.setTitle(title);
Scene scene = (Scene) context.getBean("primaryStageScene");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void launch(Class<?>... annotatedClasses) {
SpringJavaFX.annotatedClasses = annotatedClasses;
Application.launch();
}
}
Spring way building
And here is an example of spring-way building. Each component is a bean and created in place:
public class Attempt01_HelloWorld {
#Configuration
public static class Config {
#Bean
String primaryStageTitle() {
return "Attempt01_HelloWorld";
}
#Bean
Scene primaryStageScene() {
Scene ans = new Scene(root(), 800, 600);
return ans;
}
#Bean
Button button() {
Button ans = new Button();
ans.setText("Say 'Hello World'");
ans.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
root().getChildren().add(ans);
return ans;
}
#Bean
StackPane root() {
StackPane root = new StackPane();
return root;
}
}
public static void main(String[] args) {
SpringJavaFX.launch(Config.class);
}
}
I'm not sure it would work, but you could try and add setter methods for the app's parameters inside the inner class, and try and call them from outside (e.g. from your main). Again, I don't know whether this would work or not, but I'd give it a try in your place.
I have multiple controllers, each associated to a different FXML file. There is an event in one node that requires synchronization across other nodes, so I decided to do this with another event, and event handlers in the various controller files.
To register the event handlers requires the event handler method to be static (i.e., addEventHandler(SomeEvent, ClassName::MethodName).
So, the controller looks something like...
public class MyController {
private static MyController selfRef = null;
public MyController() {
selfRef = this;
}
public static void someEventHandler(Event event) {
if (selfRef != null) {
selfRef.doSomethingUseful();
}
}
private void doSomethingUseful() { /* synch the nodes */ }
}
This works, but seems a bit of a hack. Is there a preferred mechanism to achieve the same end result?
You might have more flexibility with this if you get rid of all the static stuff and make the event handler a member of your controller class as demonstrated below.
Sample implementation without static members
import javafx.event.*;
import javafx.fxml.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.*;
import java.io.IOException;
class CustomerDialogController {
#FXML
private Label customerName;
private EventHandler<Event> customEventHandler = event -> {
// handle the event...
};
void initData(Customer customer) {
customerName.setText(customer.getName());
}
public EventHandler<Event> getCustomEventHandler() {
return customEventHandler;
}
}
public class EventHandling {
public Stage showCustomerDialog(Customer customer) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("customerDialog.fxml"));
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(new Scene(loader.load()));
CustomerDialogController controller = loader.getController();
controller.initData(customer);
stage.addEventHandler(Event.ANY, controller.getCustomEventHandler());
stage.show();
return stage;
}
}
class Customer {
private String name;
Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Notes on implementation options
In the example the event handler has been added to the stage, but it could equally have been added to any scene or node or anything that has the ability to handle events.
If desired, you could also add a setter for the event handler to allow changing of the event handling logic externally.
In addition to the setup above you might wish to have the controller self-register the event handler in it's initialize method. Whether you do so or not just depends on whether you want the ability to register event handlers exposed outside the controller or if you want to use encapsulation to hide all of the event handling logic local to the controller.
Notes on (perhaps superior) alternatives
As an alternate approach, rather than using the event handling system within JavaFX for your custom approach, you could make use of a third party system such as the Google Guava Event Bus.
You should also consider why you need to add custom event handling to your application. JavaFX supports very flexible binding and observer patterns. By exposing properties of your model objects as observable, it is often not necessary to have custom events. Often, your view controllers can observe any changes to associated model objects and modify the internal state of model objects based upon UI interactions. This is especially the case if you introduce a dependency injection based system for injecting models into your controllers, such as Guice, Spring, afterburner.fx or Gluon Ignite.
Maybe you could use some kind of registry, which takes care of the synchronisation. Here is a quick and dirty example:
public class Synchronizer {
private ObservableList<Node> nodes;
private boolean isSyncing;
public Synchronizer() {
nodes = FXCollections.observableArrayList();
}
public void addNode(Node node) {
nodes.add(node);
}
public void sync(Node sourceNode, Event event) {
if (isSyncing) {
return;
}
isSyncing = true;
for (Node node : nodes) {
if (node != sourceNode) {
node.fireEvent(event);
}
}
isSyncing = false;
}
}
In your Controller you can add the node, whose event you like to get synchronized, to the synchronizer, and call sync() in the eventListener.
public class Controller {
private StackPane root;
private Button button;
public Controller(Synchronizer synchronizer) {
button = new Button();
button.setOnAction(evt -> {
synchronizer.sync(button, evt);
//action
});
synchronizer.addNode(button);
root = new StackPane(button);
}
}
EDIT:
This should make for a cleaner version:
public class Starter extends Application {
#Override
public void start(Stage primaryStage) {
ViewController controller1 = new ViewController();
ViewController controller2 = new ViewController();
Synchronizer synchronizer = new Synchronizer();
synchronizer.add(controller1);
synchronizer.add(controller2);
VBox box = new VBox(controller1.root, controller2.root);
primaryStage.setScene(new Scene(box));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public interface SyncTarget {
Node getSyncNode();
void triggerAction();
}
public class Synchronizer {
private ObservableList<SyncTarget> syncTargets;
private EventHandler<Event> eventHandler;
public Synchronizer() {
syncTargets = FXCollections.observableArrayList();
eventHandler = e -> sync();
}
public void add(SyncTarget target) {
syncTargets.add(target);
target.getSyncNode().addEventHandler(ActionEvent.ANY, eventHandler);
}
public void remove(SyncTarget target) {
syncTargets.remove(target);
target.getSyncNode().removeEventHandler(ActionEvent.ANY, eventHandler);
}
public void sync() {
for (SyncTarget target : syncTargets) {
target.triggerAction();
}
}
}
public class ViewController implements SyncTarget {
private StackPane root;
private Button button;
public ViewController() {
button = new Button();
root = new StackPane(button);
}
#Override
public Node getSyncNode() {
return button;
}
#Override
public void triggerAction() {
//action
}
}
}
I want to create a simple reusable custom control in JavaFX that is nothing more than a ComboBox with a label over its head that can have the text set.
I would like for it to be usable in JavaFX Scene Builder.
I would also like for it to be able to take a single Generic Parameter <T> to be able to as closely as possible emulate the behavior of the standard ComboBox that is available.
The problem which I am encountering is that when I attempt to set the Controls Controller to Controller<T> in SceneBuilder, I get an error telling me: Controller<T> is invalid for Controller class.
This makes sense as when you call FXMLLoader.load() (after setting the root, classLoader, and Location), there is no way (that I can find) to tell the loader "Oh, and this is a CustomControl."
This is the code I have for the Control:
public class LabeledComboBox<T> extends VBox {
private final LCBController<T> Controller;
public LabeledComboBox(){
this.Controller = this.Load();
}
private LCBController Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("LabeledComboBox.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch (IOException ex){
throw new IllegalStateException(ex);
}
final LCBController ctrlr = loader.getController();
assert ctrlr != null;
return ctrlr;
}
/*Methods*/
}
This is the Controller class:
public class LCBController<T> implements Initializable {
//<editor-fold defaultstate="collapsed" desc="Variables">
#FXML private ResourceBundle resources;
#FXML private URL location;
#FXML private Label lbl; // Value injected by FXMLLoader
#FXML private ComboBox<T> cbx; // Value injected by FXMLLoader
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Initialization">
#Override public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
this.location = fxmlFileLocation;
this.resources = resources;
//<editor-fold defaultstate="collapsed" desc="Assertions" defaultstate="collapsed">
assert lbl != null : "fx:id=\"lbl\" was not injected: check your FXML file 'LabeledComboBox.fxml'.";
assert cbx != null : "fx:id=\"cbx\" was not injected: check your FXML file 'LabeledComboBox.fxml'.";
//</editor-fold>
}
//</editor-fold>
/*Methods*/
}
Clearly there is something that I am missing here. I am really hoping this is possible without having to come up with my own implementation of the FXMLLoader Class (REALLY, REALLY, REALLY REALLY hoping).
Can someone please tell me what I am missing, or if this is even possible?
EDIT 1:
After someone pointed me to a link I may have an idea of how to do this but I'm still not one hundred percent. To me it feels like the Controller class itself can not be created with a generic parameter (I.E.: public class Controller<T>{...} = No Good)
That's kind of annoying but I guess makes sense.
Then what about applying Generic parameters to the Methods inside the custom control controller, and making the control itself (not the controller) a generic: like so?
Control:
public class LabeledComboBox<T> extends VBox {...}
Controller:
public class LCBController implements Initializable {
/*Stuff...*/
/**
* Set the ComboBox selected value.
* #param <T>
* #param Value
*/
public <T> void setValue(T Value){
this.cbx.setValue(Value);
}
/**
* Adds a single item of type T to the ComboBox.
* #param <T> ComboBox Type
* #param Item
*/
public <T> void Add(T Item){
this.cbx.getItems().add(Item);
}
/**
* Adds a list of items of type T to the ComboBox.
* #param <T> ComboBox Type
* #param Items
*/
public <T> void Add(ObservableList<T> Items){
this.cbx.getItems().addAll(Items);
}
/**
* Removes an item of type T from the ComboBox.
* #param <T> ComboBox Type
* #param Item
* #return True if successful(?)
*/
public <T> boolean Remove(T Item){
return this.cbx.getItems().remove(Item);
}
}
Would that work? Is that more along the right track? Again, my desire is nothing more than a ComboBox with a Label on it to tell users what its all about.
This worked for me, and when I imported the library into SceneBuilder it worked fine:
(Very basic) FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ComboBox?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="VBox"
fx:controller="application.LabeledComboBoxController">
<Label fx:id="label" />
<ComboBox fx:id="comboBox" />
</fx:root>
Controller:
package application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SingleSelectionModel;
public class LabeledComboBoxController<T> {
#FXML
private Label label ;
#FXML
private ComboBox<T> comboBox ;
public void setText(String text) {
label.setText(text);
}
public String getText() {
return label.getText();
}
public StringProperty textProperty() {
return label.textProperty();
}
public ObservableList<T> getItems() {
return comboBox.getItems();
}
public void setItems(ObservableList<T> items) {
comboBox.setItems(items);
}
public boolean isWrapText() {
return label.isWrapText();
}
public void setWrapText(boolean wrapText) {
label.setWrapText(wrapText);
}
public BooleanProperty wrapTextProperty() {
return label.wrapTextProperty();
}
public SingleSelectionModel<T> getSelectionModel() {
return comboBox.getSelectionModel();
}
}
Control:
package application;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.layout.VBox;
public class LabeledComboBox<T> extends VBox {
private final LabeledComboBoxController<T> controller ;
public LabeledComboBox(ObservableList<T> items, String text) {
controller = load();
if (controller != null) {
setText(text);
setItems(items);
}
}
public LabeledComboBox(ObservableList<T> items) {
this(items, "");
}
public LabeledComboBox(String text) {
this(FXCollections.observableArrayList(), text);
}
public LabeledComboBox() {
this("");
}
private LabeledComboBoxController<T> load() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"LabeledComboBox.fxml"));
loader.setRoot(this);
loader.load();
return loader.getController() ;
} catch (Exception exc) {
Logger.getLogger("LabeledComboBox").log(Level.SEVERE,
"Exception occurred instantiating LabeledComboBox", exc);
return null ;
}
}
// Expose properties, but just delegate to controller to manage them
// (by delegating in turn to the underlying controls):
public void setText(String text) {
controller.setText(text);
}
public String getText() {
return controller.getText();
}
public StringProperty textProperty() {
return controller.textProperty();
}
public boolean isWrapText() {
return controller.isWrapText();
}
public void setWrapText(boolean wrapText) {
controller.setWrapText(wrapText);
}
public BooleanProperty wrapTextProperty() {
return controller.wrapTextProperty();
}
public ObservableList<T> getItems() {
return controller.getItems();
}
public void setItems(ObservableList<T> items) {
controller.setItems(items);
}
public SingleSelectionModel<T> getSelectionModel() {
return controller.getSelectionModel();
}
}
Test code:
package application;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
LabeledComboBox<String> comboBox = new LabeledComboBox<String>(
FXCollections.observableArrayList("One", "Two", "Three"), "Test");
root.setTop(comboBox);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I'm sure that this construction is not possible as FXML is evaluated at runtime. And generics are already deleted at runtime.
But what's possible to do is to assign a generic to the controller.
FXML implements the Model-View-Controller (MVC) design which is subject to the following topic:
What is MVC (Model View Controller)?
Your question ist also an issue in the following topic:
Setting TableView Generic Type from FXML
I've got a toolbar item with its own icon, defined in the plugin.xml file like:
<action
class="MyActionClass"
id="MyActionID"
label="MyActionLabel"
menubarPath="MyActionMenuBarPath"
toolbarPath="MyActionToolBarPath"
icon="icon/myicon.png" <---- this one
...
</action>
How do I change this dynamically when needed? I mean changing it from code
Use org.eclipse.ui.menus extension point instead and add menuContribution with dynamic. The class of dynamic should subclass ControlContribution and implement createControl method to create a button.
You should implements IElementUpdater in your Handler class.
Please refer to : https://stackoverflow.com/a/23742598/2893073
Handler class
import java.util.Map;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.menus.UIElement;
import com.packpub.e4.menu.Activator;
public class SampleHandler2 extends
AbstractHandler implements IElementUpdater{
private static ImageDescriptor image_enable =
Activator.getImageDescriptor("icons/btn_adapt_enable.png");
private static ImageDescriptor image_disable =
Activator.getImageDescriptor("icons/btn_adapt_disable.png");
/**
* The constructor.
*/
public SampleHandler2() {
}
/**
* the command has been executed, so extract extract the needed information
* from the application context.
*/
public Object execute(ExecutionEvent event) throws ExecutionException {
//...
return null;
}
#Override
public void updateElement(UIElement element, #SuppressWarnings("rawtypes") Map map) {
boolean condition = false;
//...
if( condition ) {
element.setIcon(image_disable);
}else{
element.setIcon(image_enable);
}
}
}
invoke this Handler using ICommandService:
IWorkbenchWindow window = part.getSite().getWorkbenchWindow();
ICommandService commandService = (ICommandService) window.getService(ICommandService.class);
if (commandService != null) {
commandService.refreshElements("com.packpub.e4.menu.commands.sampleCommand", null);
}
Thanks.
I have created a textfield that takes all characters from the user.. but i want to disable the space so that user cant enter space ...help??
pin = new TextField("Pin#","",4,TextField.PASSWORD);
If it's a PIN number then maybe you should replace the constraints parameter with TextField.NUMERIC | TextField.PASSWORD.
Implement the ItemStateListener interface. Then call this.setItemStateListener(this) in the Form constructor.
Implement the itemStateChanged method so that if the Item is the one you are interested in then get its content and test it if it contains spaces.
In my case I create a MIDlet and a Form which contains a TextField. And it works. I do not know why did you say that the solution I gave you did not work to you ! Here is a very simple example I give ( I created it and tested it ! ) :
package hello;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloMIDlet extends MIDlet {
public Display display;
public HelloMIDlet() {
display = Display.getDisplay(this);
}
public void startApp() {
Form f = new F(display);
display.setCurrent(f);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
package hello;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.lcdui.TextField;
public class F extends Form implements ItemStateListener {
private TextField pin = new TextField("PIN :","",4,TextField.PASSWORD);
private Alert alert;
private Display disp;
public F(Display d)
{
super("");
disp = d;
this.setItemStateListener(this);
this.append(pin);
}
public void itemStateChanged(Item item) {
if (item == pin)
{
for (int i=0; i<pin.getString().length(); i++)
{
if (String.valueOf(pin.getString().charAt(i)).equals(new String(" ")))
displayAlert();
}
}
}
private void displayAlert()
{
alert = new Alert("Info","No space please !",null, AlertType.ERROR);
disp.setCurrent(alert, this);
}
}