Peter
Peter

Reputation: 1894

Handle multiple JavaFX buttons with one controller

I'm trying to implement an MVC solution in JavaFX like I used to with swing. It seems to me that there are some differences. My usual approach was to have a Starter class to initialize a view and model and pass them both to the controller with the constructor. I would then give the view a reference to the controller so it can registrate it for the UI handling. I then would use setActionCommand("xy"); on any GUI element I need to access in the controller. setActionCommand(); ist not available for JavaFX. I've also seen posts that I should use different controller for different elements, as seen here.
I started implementing it as I'm used to and I'm now stuck at the point where I can't assign a method to a button. My 3 buttons look like this:

Button newGraph = new Button("Neuer Graph");
newGraph.setOnAction(controller);
Button extendGraph = new Button("Graph erweitern");
extendGraph.setOnAction(controller);
Button reset = new Button("Filter zurücksetzen");
reset.setOnAction(controller);

My controller looks like this:

public class GraphController implements EventHandler {
    private GraphHandler graphHandler; //model
    private GraphView graphView; //view

    public GraphController(GraphHandler graphHandler, GraphView graphView, Stage stage){
        this.graphHandler = graphHandler;
        this.graphView = graphView;
        graphView.registerController(this); //introduce controller to view
        graphView.start(stage); //start the main frame
    }

    @Override
    public void handle(Event event) {
        System.out.println(event.getSource().toString());
    }
}

Which does print:

Button@1f74b31a[styleClass=button]'Filter zurücksetzen'
Button@37901011[styleClass=button]'Neuer Graph'
Button@39afc158[styleClass=button]'Graph erweitern'

How can I distinguish the buttons without using if(event.getSource().toString().equals("Button@1f74b31a[styleClass=button]'Filter zurücksetzen'")); or somthing the like?
I'm also looking forward to use it on CheckBox and MenuItem.

Edit:
Partly solved it by parsing the sender to a button.

@Override
    public void handle(Event event) {
        try {
            Button sender = (Button) event.getSource();
            switch (sender.getText()){
                case "Filter zurücksetzen":
                    System.out.println("reset");
                    break;
                case "Neuer Graph":
                    System.out.println("new");
                    break;
                case "Graph erweitern":
                    System.out.println("extend");
                    break;
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }

Is there a more generic way? Since I don't want to parse the other events as well, which would result in more try, catch blocks. Altough this might be a question for codereview at that point..

Upvotes: 2

Views: 3324

Answers (2)

James_D
James_D

Reputation: 209339

The usual way is to use a different handler for each control. Typically this is done with lambda expressions, as in hotzst's answer; if you prefer a more legacy approach you could also just add one or more parameters to your stand alone class and use different instances of it.

public class ButtonHandler implements EventHandler<ActionEvent> {

    private final String message ;

    public ButtonHandler(String message) {
        this.message = message ;
    }

    @Override
    public handle(ActionEvent event) {
        System.out.println(message);
    }
}

and then

Button newGraph = new Button("Neuer Graph");
newGraph.setOnAction(new ButtonHandler("New"));
Button extendGraph = new Button("Graph erweitern");
extendGraph.setOnAction(new ButtonHandler("extend"));
Button reset = new Button("Filter zurücksetzen");
reset.setOnAction(new ButtonHandler("reset"));

Upvotes: 3

hotzst
hotzst

Reputation: 7496

JavaFX relies heavily on the use of lambdas. In the case were the event handler is simple (only a few lines of code) and not reused by any other controls, you can do it like this:

Button reset = new Button("Filter zurücksetzen");
reset.setOnAction(event -> System.out.println("Clicked on 'Filter zurücksetzen'"));

If the same simple lambda expression is reused you can define it separatly and reuse it:

EventHanlder handler = event -> System.out.println("Clicked on 'Filter zurücksetzen'");
Button reset = new Button("Filter zurücksetzen");
reset.setOnAction(handler);

Or if you want to have them defined in you Controller you can do that as well without the need for the Controller to implement EventHandler:

public void handleClickOnReset(Event event) {
    System.out.println("Clicked on 'Filter zurücksetzen'");
}

And then use it like this: reset.setOnAction(controller::handleClickOnReset)

Upvotes: 4

Related Questions