0

Need Your Help! This may be more of design perspective but i'm using this pattern all over my application, so wanted to get an idea.

Sample screen shot attached. https://i.stack.imgur.com/NPzIG.jpg

My Question: I have a BorderPane which is kind of root container in my case (RootBorderPane.fmxl, RootBorderPaneController.java). The view is designed using scene builder. I'm going to use the left side as a Navigation panel and based on the selection on the left side, i'll load a new view in the RootBorderPane's center area. I have other views like these (View1.fxml, View2.fxml, View3.fxml etc) (designed using scene builder)and respective controllers (View1Controller.java, View2Controller.java, View3Controller.java etc). View1, View2, View3 contains TableViews on their own. Now i have few buttons(Button1, Button2, Button3 etc) in the RootBorderPane's left and if i click Button1 and then i should load my View1(View1.fxml) and set it in the RootBorderPane center area.

I clicked "Button1", on the button click i loaded the View1 and set it on the RootBorderPane's center. This logic is inside the RootBorderPaneController.java View1 contains a TableView and the data for the table view is loaded and set on the initializable() method of the View1's controller (View1Controller.java) I am able to achive all the above said.

Problem: I have Add and Delete button on the Navigation panel and these buttons should be enabled/disabled based on the record selected on the View1's tableview. (Delete button should be enabled if a record is selected on the tableview otherwise No. Add button should be enabled if no record is selected on the tableview)

Directly or Indirectly my question shelves to accessing one controller from another controller. Is there any elegant design approach to do this instead of keeping of 1 controller's reference in the other controller.

Thanks in Advance

Saravana Kumar M
  • 460
  • 9
  • 19

2 Answers2

0

one way to let you access the controller of your subPanes (View_1...View_n) is to load them as you do it for your root pane.

// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
rootLayout = loader.load( getClass().getResource( "BorderPane.fxml").openStream());

BorderPaneController bpc = (BorderPaneController) loader.getController();

List<Pane> subPaneList = new ArrayList<>();
for ( int i = 0; i < 30; i++) {
    loader = new FXMLLoader();
    loader.setLocation( MainApp.class.getResource( "View" + i + ".fxml"));
    subPaneList.add( (Pane) loader.load());
}
bpc.setSubPaneList( subPaneList);

// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();

now you just need a setSubPaneList(..) method in your bpc controller and you will be able to access the public methods (e.g. getter/setter) of your subPanes

Prunkton
  • 41
  • 5
0

It seems that you load dynamically the View using FXML Loader, so here are the rule for using it:

  1. Don't use static method of FXMLLoader, intanciate the FXMLLoader to have more options on it (i.e. the controller & node instance)
  2. Get the instance of the child Controller given by the FXMLLoader, from the main Controller.
  3. choose a common way to operate between parent-child controllers: I propose a selectedProperty() method.
  4. Get this property from an interface so it is accessible for all child controllers

I use Property to operate between controllers, in your case the selectedProperty of TableView should be the ideal solution: you can have the selected instance, and you have the event of selection/deselection, that can be used in 2 ways: just bind it, or add a listener of the changement done in tableView.
For this case, the bind is enough.

There are many files involved in that solution, so here is the main trick: From the main controller, have the instance of the selectedProperty of the current child, and bind it to the visibleProperty of your button, to show it.

buttonOpen.disableProperty().bind(controller.selectedProperty().isNull());

You will need that selectedProperty again to in the code of the open button

public void open(){
 //TODO open in a view
 System.out.println("open the object"+selectedProperty().get()
}

Full sources:

Java files

/**
 * App launch
 */
public class SOFx extends Application{

  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(Stage primaryStage) throws Exception {
    primaryStage.setTitle ("SO test");
    //static loading for index, don't need the controller.
    Parent root =  FXMLLoader.load(getClass().getResource("Index.fxml"),null);
    Scene scene = new Scene(root);
    primaryStage.setMaximized(true);
    primaryStage.setScene(scene);
    primaryStage.show ();
  }
}

/**
 * Main Controller
 */
public class IndexController {


  @FXML
  private BorderPane root;

  @FXML
  private Button buttonOpen;

  private ObjectProperty<Object> selectedProperty;

  public FXMLLoader loadFXML(URL url, ResourceBundle resources){
    FXMLLoader fxmlLoader = new FXMLLoader ();
    fxmlLoader.setLocation (url);
    fxmlLoader.setResources(resources);
    try {
      fxmlLoader.load ();
      return fxmlLoader;
    } catch (IOException e) {
      e.printStackTrace(System.err);
      throw new IllegalStateException(e);
    }
  }

  public void button1(){
    FXMLLoader loadFXML = loadFXML(getClass().getResource("View1.fxml"),null);
    root.setCenter(loadFXML.getRoot());
    ICenterController controller = (ICenterController) loadFXML.getController();
    selectedProperty = controller.selectedProperty();
    buttonOpen.disableProperty().bind(selectedProperty.isNull());
  }

  public void open(){
    //TODO open in a view
    System.out.println("open the object"+selectedProperty.get());
   }

}

/**
 * Interface of Center controllers
 */
public interface ICenterController {
 ObjectProperty<Object>  selectedProperty();
}



/**
 * Child controller
 */
public class View1Controller implements ICenterController {

  private static final ObjectProperty<Object> selectedProperty = new SimpleObjectProperty();

  @Override
  public ObjectProperty<Object> selectedProperty() {
    return selectedProperty;
  }

  /**
   * simulate TableView selection/deselection
   */
  public void select(){
    selectedProperty.set(new Object());
  }

  public void deselect(){
    selectedProperty.set(null);
  }

}

Fxml files, I don't do a full TableView, I simulate it with buttons

Index.fxml

    <BorderPane xmlns="http://javafx.com/javafx/8.0.51" xmlns:fx="http://javafx.com/fxml/1"
        fx:controller="pdem.stackoverflow.IndexController" fx:id="root">

        <left>
            <VBox>
                <Button onAction="#button1" text="view1"/>
                <Button fx:id="buttonOpen" text="open"/>
            </VBox>

        </left>


    </BorderPane>

View1.fxml

<VBox xmlns="http://javafx.com/javafx/8.0.51" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="pdem.stackoverflow.View1Controller" >
            <Button onAction="#select" text="select"/>
            <Button onAction="#deselect" text="deselect"/>
</VBox>
pdem
  • 3,880
  • 1
  • 24
  • 38