Please help me solve the problem. There are two fxml files and their controllers:
sample.fxml, its controller ControllerMain (main window of the program)
find_win.fxml, its ControllerFind controller (modal window "text search")
In the modal window find_win.fxml there is a TextField into which the search text is entered, and the Find button, when clicked, ControllerFind must process the click and call the search method and highlight the search text in the TextArea element of the sample.fxml window.
<fx: include source = "sample.fxml" fx: id = "textAreaOne" />
and the inheritance of the ControllerMain by the ControllerFind controller does not help to achieve a solution - in the first case the entire window markup is included in the modal window completely, in the second case, the java.lang.reflect.InvocationTargetException is returned during an operation on the TextArea.
How to implement actions on elements of one window from another window?
I've found a solution elsewhere. Thanks to Comrade Antizam, who tried to help, but did not quite understand what I needed.
The solution described here is .
In short, it is necessary to create an instance of a controller-parent and a method that takes an instance of a controller-parent as a parameter in the controller-child. When a new window is opened from a controller-parent, get an instance of the controller-child and indicate to it through the created method "this".
Further, in the controller-child, it will be possible to access the elements of the parent controller.
controller-parent:
package sample;
public class ControllerMain {
private ControllerFind children; // controller-child
//main-window
#FXML
public TextArea textAreaOne;
#FXML
public MenuItem findMenuItem;
public void findAction(ActionEvent actionEvent) {
try {
Stage stageFind = new Stage();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML/find_win.fxml"));
Parent root = loader.load();
stageFind.setTitle("Find");
stageFind.setMinHeight(200);
stageFind.setMinWidth(150);
stageFind.setResizable(false);
stageFind.setScene(new Scene(root));
stageFind.getIcons().add(new Image("image/search.png"));
stageFind.initModality(Modality.NONE);
stageFind.show();
children = loader.getController(); //getting controller of window find_win.fxml
children.setParent(this); //setting parent of the controller-child - this
} catch (IOException e) {
e.printStackTrace();
}
}
}
controller-child:
package sample.Controllers;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import sample.Classes.DialogManager;
import sample.ControllerMain;
public class ControllerFind {
//Window "Find"
#FXML public TextField searchTextField;
#FXML public Label findTextLabel;
#FXML public Button okTextFindButton;
#FXML public Button cancelTextFindButton;
private String text;
private ControllerMain controller;
public void setParent (ControllerMain controller){
this.controller = controller;
}
public ControllerFind getThis(){
return this;
}
public void initialize(){
System.out.println("psvm");
}
public void textFindOkButtonAction(ActionEvent actionEvent) {
text = (searchTextField.getText());
if (text.equals("")) {
DialogManager.showInfoDialog("Error!", "Enter text what you are looking for!");
} else {
if (controller.textAreaOne.getText() != null && !controller.textAreaOne.getText().isEmpty()) {
int index = controller.textAreaOne.getText().indexOf(text);
if (index == -1) {
DialogManager.showInfoDialog("Result", "There isn't text what you are looking for");
} else {
controller.textAreaOne.selectRange(index, index + text.length());
}
} else {
DialogManager.showInfoDialog("Error", "TextArea is empty!");
}
}
}
public void textFindCancelButtonAction(ActionEvent actionEvent) {
Node source = (Node) actionEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
}
}
Vis-a-vis windows communication in this situation, it's optimal to use TextInputDialog something like this:
#Override
public void start(Stage primaryStage){
Button btn=new Button("Click");
VBox vbox=new VBox();
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(btn);
Scene scene=new Scene(vbox, 200,200);
btn.setOnAction(e->
{
TextInputDialog dialog=new TextInputDialog();
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
System.out.println(result.get());
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
but text highlighting is not something that can be done easy.
You could do String search of TextArea text, compare it to result from another window then highlight it via
textArea.getSelectedRange(firstIndex, lastIndex);
firstIndex and lastIndex being indexes of textArea text for the word we are searching. Then having button on every click displaying next word occurancy inside text and highlighting it. But if you insist on highlighting every instance of word at the same time I would suggest using RichTextFX.
Related
I am in the process of learning Java and I can't seem to make this function work. I am working on a project for an inventory system. Where parts are added to an observable list. In the add part screen, I have two radio buttons to select whether the part is an InHouse made or OutSourced. I have an if statement using a toggle group to check which radio button is selected. I was trying to use an else statement to pass the values if it was an outsourced part. Right now it will pass the values correctly if the radio button is set to InHouse, but when set to Outsource it throws a null pointer exception at the line the if statement is on.
Here is the code I am trying to make work.
package ViewController;
import Model.InHouse;
import Model.Inventory;
import Model.OutSource;
import Model.Part;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
public class AddPart implements Initializable {
public RadioButton addPartInHouse;
public TextField partIDTxtbox;
public TextField partNameTxtbox;
public TextField invTxtbox;
public TextField pricePerUnitTxtbox;
public TextField maxQtyTxtbox;
public TextField minQtyTxtbox;
public Button addPartSaveButton;
public Button addPartCancelButton;
public RadioButton addPartOutSource;
public Label companyNameLabel;
public TextField companyNamePrompt;
public ToggleGroup switchfield;
public void addPartInHouse(ActionEvent actionEvent) {
companyNameLabel.setText("Machine ID");
companyNamePrompt.setPromptText("Machine ID");
addPartOutSource.setSelected(false);
}
public void addPartOutsource(ActionEvent actionEvent) {
companyNameLabel.setText("Company Name");
companyNamePrompt.setPromptText("Company Name");
addPartInHouse.setSelected(false);
}
public void addPartSaveButton(ActionEvent e) throws IOException {
/*this is my line 55 below*/
if (this.switchfield.getSelectedToggle().equals(this.addPartInHouse)) {
Part partadd = new InHouse(0, "", 0.0, 0,0,0,0);
if(partIDTxtbox.getText().isEmpty()){
partadd.setPartID(Inventory.getPartIDCount());
}
if (!partNameTxtbox.getText().isEmpty()){
partadd.setPartName(partNameTxtbox.getText());
}
if (!pricePerUnitTxtbox.getText().isEmpty()){
partadd.setPartPrice(Double.parseDouble(pricePerUnitTxtbox.getText()));
}
if (!invTxtbox.getText().isEmpty()){
partadd.setPartInStock(Integer.parseInt(invTxtbox.getText()));
}
if(!minQtyTxtbox.getText().isEmpty()){
partadd.setMin(Integer.parseInt(minQtyTxtbox.getText()));
}
if(!maxQtyTxtbox.getText().isEmpty()){
partadd.setMax(Integer.parseInt(maxQtyTxtbox.getText()));
}
if(!companyNamePrompt.getText().isEmpty()){
((InHouse)partadd).setMachineID(Integer.parseInt(companyNamePrompt.getText()));
}
Inventory.addPart(partadd);
}
else{
Part partaddout = new OutSource(0, "", 0.0, 0,0,0,"");
if(partIDTxtbox.getText().isEmpty()){
partaddout.setPartID(Inventory.getPartIDCount());
}
if (!partNameTxtbox.getText().isEmpty()){
partaddout.setPartName(partNameTxtbox.getText());
}
if (!pricePerUnitTxtbox.getText().isEmpty()){
partaddout.setPartPrice(Double.parseDouble(pricePerUnitTxtbox.getText()));
}
if (!invTxtbox.getText().isEmpty()){
partaddout.setPartInStock(Integer.parseInt(invTxtbox.getText()));
}
if(!minQtyTxtbox.getText().isEmpty()){
partaddout.setMin(Integer.parseInt(minQtyTxtbox.getText()));
}
if(!maxQtyTxtbox.getText().isEmpty()){
partaddout.setMax(Integer.parseInt(maxQtyTxtbox.getText()));
}
if(!companyNamePrompt.getText().isEmpty()){
((OutSource)partaddout).setCompanyName((companyNamePrompt.getText()));
}
Inventory.addPart(partaddout);
}
Parent addPartSave = FXMLLoader.load(getClass().getResource("MainScreen.fxml"));
Scene scene = new Scene(addPartSave);
Stage window = (Stage) ((Node) e.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
public void addPartCancelButton(MouseEvent mouseEvent) throws IOException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Cancel add part");
alert.setHeaderText("You are about to return to the Main screen!");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
Parent addPartCancel = FXMLLoader.load(getClass().getResource("MainScreen.fxml"));
Scene scene = new Scene(addPartCancel);
Stage window = (Stage) ((Node) mouseEvent.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
else{
return;
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
switchfield = new ToggleGroup();
this.addPartInHouse.setToggleGroup(switchfield);
}
}
'''
This is not an answer but it is more readable as an answer than as a comment.
Either switchfield is null or switchfield.getSelectedToggle() returns null.
Add the following line of code before line 55.
System.out.println(switchfield)
Then run your code. If it shows that switchfield is not null, then change it to
System.out.println(switchfield.getSelectedToggle())
And run the code again.
That should show that switchfield.getSelectedToggle() returns null.
Then you need to look at the code for method getSelectedToggle() in class ToggleGroup and see when that method returns null.
My guess is that method addPartSaveButton() is being called before you have selected one of the radio buttons.
I am brand-new to programming and only started learning this past 2 weeks, so I'm sorry for any redundant or sloppy code...
I have 2 scenes, which are in my Main class. But I'm using FXML to develop each scene, and all code has been placed in the first scene's FXML Controller. I'm ready to start building my second scene, but don't know how to properly launch it.
My question is, how can I set the stage to show the second scene (mainCallWindow), specifically from within the first FXML file's controller class. If there is a bettery way, please let me know.
Main Class:
package supportTool;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
public String versionNumber = "v2.1";
#Override
public void start(Stage primaryStage) throws Exception {
// SETTING UP THE STAGE
Stage window;
window = primaryStage;
window.setTitle("Support Tool " + versionNumber);
// SETTING UP THE SCENES
Parent newCallDetailsFXML = FXMLLoader.load(getClass().getResource("newCallDetails.fxml"));
Parent mainCallWindowFXML = FXMLLoader.load(getClass().getResource("mainCallWindow.fxml"));
Scene newCallDetails = new Scene (newCallDetailsFXML, 800, 600);
Scene mainCallWindow = new Scene (mainCallWindowFXML, 800, 600);
// CHOOSING THE SCENE AND SHOWING THE STAGE
window.setScene(newCallDetails);
window.show();
}
}
Scene 1 FXML Controller:
package supportTool;
import javafx.scene.control.*;
import javafx.scene.image.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class newCallController {
private int maxChar;
public ChoiceBox choiceAccount;
public ImageView btnCall;
public TextField tfCallbackNumber;
public TextField tfCallerName;
public TextField tfStoreNumber;
// ACTION COMPLETED WHEN CALL BUTTON IS PRESSED
public void btnCall() {
Caller newCaller = new Caller();
newCaller.setCallerName(tfCallerName.getText());
newCaller.setCallbackNumber(tfCallbackNumber.getText());
newCaller.setAccount(String.valueOf(choiceAccount.getValue()));
newCaller.setStoreNumber(tfStoreNumber.getText());
try {
FileOutputStream fos = new FileOutputStream("caller.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(newCaller);
oos.close();
fos.close();
} catch(IOException e){
e.printStackTrace();
}
// RIGHT HERE IS WHERE I WANT TO SET THE SCENE TO "mainCallWindow"
}
// CHECKS TO SEE IF THE TEXT CONTAINS ONLY LETTERS
private boolean isNumberCheckEvent(String message) {
if (message.matches("[0-9]+")) {
return true;
} else {
return false;
}
}
// SETS THE MAX CHARACTERS FOR ALL TEXTFIELDS
public void maxCharEvent() {
// CALLER NAME MAX CHARACTERS
tfCallerName.setOnKeyTyped(maxCharEvent -> {
maxChar = 20;
if(tfCallerName.getText().length() >= maxChar) {
maxCharEvent.consume();
}
});
// CALLBACK NUMBER MAX CHARACTERS
tfCallbackNumber.setOnKeyTyped(maxCharEvent -> {
maxChar = 10;
if(tfCallbackNumber.getText().length() >= maxChar) {
maxCharEvent.consume();
}
});
// STORE NUMBER MAX CHARACTERS
tfStoreNumber.setOnKeyTyped(maxCharEvent -> {
maxChar = 5;
if (String.valueOf(choiceAccount.getValue()).equals("6 Digit Account")) {
maxChar = 6;
}
if (tfStoreNumber.getText().length() >= maxChar) {
maxCharEvent.consume();
}
});
}
// CHANGES TEXT TO ONLY LETTERS BASED ON isNumberCheckEvent
public void numberValidationEvent() {
tfCallbackNumber.setOnKeyReleased(numberValidationEvent -> {
maxCharEvent();
if(tfCallbackNumber.getText().length() > 0) {
if (!isNumberCheckEvent(tfCallbackNumber.getText())) {
tfCallbackNumber.setText(tfCallbackNumber.getText().substring(0, tfCallbackNumber.getText().length() - 1));
tfCallbackNumber.positionCaret(10);
numberValidationEvent.consume();
}
}
});
tfStoreNumber.setOnKeyReleased(numberValidationEvent -> {
maxCharEvent();
if(tfStoreNumber.getText().length() > 0) {
if (!isNumberCheckEvent(tfStoreNumber.getText())) {
tfStoreNumber.setText(tfStoreNumber.getText().substring(0, tfStoreNumber.getText().length() - 1));
tfStoreNumber.positionCaret(10);
numberValidationEvent.consume();
}
}
});
}
}
You can change scene in various ways. Within your current situation you can try something like below. First, you need reference to your FXMLLoader, scene and stage to change your scene from your controller. Instead of loading in main class, do loading in your controller class.
FXMLLoader loader = new FXMLLoader(getClass().getResource("mainCallWindow.fxml"));
Parent mainCallWindowFXML = loader.load();
//use one of components on your scene to get a reference to your scene object.
Stage stage = (Stage)tfCallerName.getScene.getWindow();//or use any other component in your controller
Scene mainCallWindow = new Scene (mainCallWindowFXML, 800, 600);
stage.setScene(newCallDetails);
stage.show(); //this line may be unnecessary since you are using the same stage.
}
This is not the only way to achieve this. You can use same scene to load different FXML files. I would suggest changing the root node of a scene instead of changing scene completely.
I have two separate views one which contains a button say view A and another a TabPane with 3 tabs call it view B. These two views are controlled by two separate view controller classes. I want to be able to click a button in view A and be able to to open a specific tab in view B's TabPane.
So far I have tried extending the controller for view A with view B such that I can get the TabPane defined in view B's controller then call myTabPane.getSelectionModel().select(myTab); however this hasn't worked as it throws a NullPointerException.
My question is it possible to click a button in view A such that it opens view B and opens a specific Tab on view B's TabPane.
I have also looked at these links with no luck 1. setting selected tab, 2. switch through tabs programatically, 3. switch between tabs in tabpane
Lets say the above image is view A and when u click right it should open view B and open a specific tab in view B's TabPane.
Lets say the above image is view B and when the button right on view A is clicked it should open view B and set the tab to tab C.
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.scene.control.SelectionModel;
public class SucessfulCreateProjectViewController extends AdminViewController {
#FXML
private Button OkButton;
#FXML
void handleCreateTasksButtonAction(ActionEvent event) {
try{
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("AdminView.fxml"));
Scene scene = new Scene(fxmlLoader.load());
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
AdminTabPane.getSelectionModel().select(1);;
}catch(Exception e){
ErrorHandlerController.infoBox("Error Opening AdminPage", "Fail", null);
e.printStackTrace();
}
}
#FXML
void handleOKButtonAction(ActionEvent event) {
Stage stage = (Stage) OkButton.getScene().getWindow();
stage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
} }
Mediator is a design pattern that is used to communicate between objects that do not know each other.
This is an example of a mediator that would have done your job.
public class Mediator {
private static Mediator instance;
private Consumer<String> consumer;
public static Mediator getInstance() {
if(instance == null) {
instance = new Mediator();
}
return instance;
}
private Mediator() {
}
public void register(Consumer<String> consumer) {
this.consumer = consumer;
}
public void fireEvent(String string) {
if(consumer != null) {
consumer.accept(string);
}
}
}
and respectively, the two controllers
public class ViewAController {
#FXML
private Button btnL, btnR;
#FXML
private void initialize() {
btnL.setOnAction(event -> Mediator.getInstance().fireEvent("left"));
btnR.setOnAction(event -> Mediator.getInstance().fireEvent("right"));
}
}
public class ViewBController {
#FXML
private TabPane tabPane;
#FXML
private void initialize() {
Mediator.getInstance().register(s -> {
switch (s) {
case "left":
tabPane.getSelectionModel().select(0);
break;
case "right":
tabPane.getSelectionModel().select(2);
break;
}
});
}
}
and this is a test application that opens two windows at once.
public class Main extends Application {
#Override
public void start(Stage stageA) throws Exception{
Parent viewA = FXMLLoader.load(getClass().getResource("view_a.fxml"));
Parent viewB = FXMLLoader.load(getClass().getResource("view_b.fxml"));
stageA.setTitle("View A");
stageA.setScene(new Scene(viewA));
stageA.show();
Stage stageB = new Stage();
stageB.setTitle("View B");
stageB.setScene(new Scene(viewB));
stageB.show();
}
public static void main(String[] args) {
launch(args);
}
}
if you do not want to use two windows at once, just change the middleman.
I am currently trying to build an application which behaves similar to a command shell. I want to display a path that I give it (or a '>' character at the very least) before the user's input text in a javaFX text field. like this:
I have it so that the text field will clear when the user submits the text. After a submission it sets the text of the field to be my path to achieve a similar effect, but the user can still delete this path while inputting text.
How can I make it so that my path text appears in the field but the user cannot delete it?
I've tried this but it only updates the caret position after submission:
textField.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
textField.positionCaret(textField.getLength());
}
});
You can use a TextFormatter to filter out invalid operations on the text field. A TextFormatter has a filter which filters changes to the text field; you can veto any changes by having the filter return null. The simplest implementation for what you describe would just filter out any changes where the caret position or the anchor for the text field were before the end of the fixed text:
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.getCaretPosition() < prefix.length() || c.getAnchor() < prefix.length()) {
return null ;
} else {
return c ;
}
};
textField.setTextFormatter(new TextFormatter<String>(filter));
You can experiment with other logic here (for example if you want the user to be able to select the fixed text).
Here is a SSCCE:
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TextFieldFixedPrefix extends Application {
private TextField createFixedPrefixTextField(String prefix) {
TextField textField = new TextField(prefix);
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.getCaretPosition() < prefix.length() || c.getAnchor() < prefix.length()) {
return null ;
} else {
return c ;
}
};
textField.setTextFormatter(new TextFormatter<String>(filter));
textField.positionCaret(prefix.length());
return textField ;
}
#Override
public void start(Stage primaryStage) {
TextField textField = createFixedPrefixTextField("/home/currentUser $ ");
StackPane root = new StackPane(textField);
Scene scene = new Scene(root, 300,40);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a JavaFX user interface with several controls; the values should be stored inside fields of a Model class; the UI class has a reference to Model.
Say the Model class is the basic:
public static class Model{String myText; /*javabeans getters and setters provided too*/}
The JavaFX Application is the following.
public class T08 extends Application {
Model model;
#Override
public void start(Stage primaryStage) throws Exception {
model = new Model();
BorderPane bp = new BorderPane();
primaryStage.setScene(new Scene(bp));
//this is the component that should be connected to model.myText
TextField textField = new TextField();
bp.setCenter(textField);
primaryStage.show();
}
Question
The user can write text in textField control and the text should be saved into model.myText.
During application startup i need to load the data into the Model and have it rendered to the controls.
I've tried with JavaFX 2.x bindings, but they seem to focus on unidirectional connections.
What are my options to accomplish this in a neat way?
One way is to use myTextProperty.bindBidirectional() instead of myTextProperty.bind(), AFAIK.
Ok I wrote some SSCCE sample code, since code explains the things more precisely :)
First example is for this question:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class JavaFXApplication10 extends Application {
private Model model = new Model();
#Override
public void start(Stage primaryStage) {
final TextField textField = new TextField();
textField.setText(model.getMyText());
Button btn = new Button();
btn.setText("Done");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
model.setMyText(textField.getText());
System.out.println("Done.");
System.out.println("New value: " + model.getMyText());
}
});
BorderPane root = new BorderPane();
root.setTop(textField);
root.setBottom(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// Data Model
public static class Model {
private String myText = "model myText default";
public String getMyText() {
return myText;
}
public void setMyText(String myText) {
this.myText = myText;
}
}
}
The second modified version (uses bidirectional binding) of this example is here.
In both examples try to click the button.
Maybe you take a look at https://github.com/laubfall/modelfx. I wrote this library exactly for this reason. It supports bidirectional Bindings for JavaFX-Components (that are composed in a hierarchy) and Properties that resides in a Bean. For further Documentation refer to the Wiki-Pages of the Github-Project modelfx. Hope you find it helpful!
ps: the core functionality uses Bindings.bindBidrectional of JavaFX.