1

this is a homework btw,

I am asked to make a jframe containing multiple jpanels which have buttons and action listeners attached to them. I have to use the MVC model to do it but, since my buttons/actions are in jpanels instead of the jframe, i do not know how to recover them. I wont put down all of my code but, just what is needed to see what I try to do. I want to get the button "ajouter" from panel 3 first to do whatever action:

So this is pannel 3

import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JPanel;

import ca.uqam.inf2120.tp2.modele.GestionAbsenceEmployes;

public class RechercherAbsenceP3 extends JPanel {

    private GestionAbsenceEmployes aControleur;
    private JButton ajouter, modifier, supprimer, afficher, fermer;
    FlowLayout gestionnaireComposant;

    RechercherAbsenceP3() {
    try {
        jbInitP3();
    } catch (Exception e) {
        e.printStackTrace();
    }
        ajouter.addActionListener(aControleur);
        modifier.addActionListener(aControleur);
        supprimer.addActionListener(aControleur);
        afficher.addActionListener(aControleur);
        fermer.addActionListener(aControleur);
    }

    private void jbInitP3() throws Exception {

    gestionnaireComposant = new FlowLayout(FlowLayout.RIGHT);

    this.setLayout(gestionnaireComposant);

    ajouter = new JButton("Ajouter");
    modifier = new JButton("Modifier");
    modifier.setEnabled(false);
    supprimer = new JButton("Supprimer");
    supprimer.setEnabled(false);
    afficher = new JButton("Afficher");
    afficher.setEnabled(false);
    fermer = new JButton("Fermer");

    this.add(ajouter);
    this.add(modifier);
    this.add(supprimer);
    this.add(afficher);
    this.add(fermer);

    }

    public JButton getAjouter() {
        return ajouter;
    }
}

This is the window

package ca.uqam.inf2120.tp2.interfacegraphique;

import java.awt.BorderLayout;
import ca.uqam.inf2120.tp2.interfacegraphique.RechercherAbsenceP3;
import javax.swing.JFrame;
import javax.swing.JPanel;

import ca.uqam.inf2120.tp2.modele.GestionAbsenceEmployes;

public class CreerRechercherAbsence extends JFrame {

    private GestionAbsenceEmployes aControleur;
    private JPanel absenceP1, absenceP2, absenceP3;

    private BorderLayout gestionnaireComposant;

    public CreerRechercherAbsence() {
        super("Gestionnaire des employés absents");

        try {
            jbInit();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        aControleur = new GestionAbsenceEmployes(this); 

    }

    void jbInit() throws Exception {

        gestionnaireComposant = new BorderLayout(5, 5);
        this.getContentPane().setLayout(gestionnaireComposant);

        absenceP1 = new RechercherAbsenceP1();
        absenceP2 = new RechercherAbsenceP2();
        absenceP3 = new RechercherAbsenceP3();

        this.getContentPane().add(absenceP1, BorderLayout.NORTH);
        this.getContentPane().add(absenceP2, BorderLayout.CENTER);
        this.getContentPane().add(absenceP3, BorderLayout.SOUTH);
    }

}

now the not finished controler:

    package ca.uqam.inf2120.tp2.modele;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import ca.uqam.inf2120.tp1.partie1.adt.impl.ListeAdtArrayListImpl;
    import ca.uqam.inf2120.tp2.interfacegraphique.CreerRechercherAbsence;

    public class GestionAbsenceEmployes implements ActionListener{

        private AbsenceEmploye modele;
        private CreerRechercherAbsence vue;

        public GestionAbsenceEmployes(CreerRechercherAbsence uneVue) {
            this.modele = new AbsenceEmploye();
            vue = uneVue;
        }

        public AbsenceEmploye getModele() {
            return modele;
        }


        @Override
        public void actionPerformed(ActionEvent event) {
            Object source = event.getSource();
            if(source == vue.getAjouter()) {

            }
        }

}

When I add the vue.getAjouter() it does not know what it is !!!

What do I do/get wrong ?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Mobidoy
  • 353
  • 1
  • 3
  • 11

3 Answers3

2

The problem is you are calling getAjouter() on CreerRechercherAbsence JFrame instance in your ActionListener where as you'd want to be calling getAjouter() on RechercherAbsenceP3 JPanel instance.

My solution:

Convert your ActionListener class GestionAbsenceEmployes to accept RechercherAbsenceP3 as the parameter so we can call getAjouter() on its instance like so:

class GestionAbsenceEmployes implements ActionListener {

    private AbsenceEmploye modele;
    private RechercherAbsenceP3 vue;

    public GestionAbsenceEmployes(RechercherAbsenceP3 uneVue) {
        this.modele = new AbsenceEmploye();
        vue = uneVue;
    }

    public AbsenceEmploye getModele() {
        return modele;
   }

    @Override
    public void actionPerformed(ActionEvent event) {
        Object source = event.getSource();
        if (source == vue.getAjouter()) {
        }
    }
}

you would than do:

    aControleur = new GestionAbsenceEmployes(absenceP3);

but in order for the above statement to function you must change this:

private JPanel absenceP1, absenceP2,absenceP3;

in CreerRechercherAbsence class to this:

private JPanel absenceP1, absenceP2;
private RechercherAbsenceP3 absenceP3;

because you extend JPanel to add functionality hence the RechercherAbsenceP3 but by declaring it as JPanel you down cast it, thus it does not have access to the methods of extended JPanel RechercherAbsenceP3 and only those of default JPanel.

Some other suggestions:

  • Do not extend JFrame class unnecessarily

  • No need for getContentPane.add(..) as add(..) has been diverted to contentPane

  • Be sure to create and manipulate Swing components on Event Dispatch Thread

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • I have buttons on other Jpanel that I will need to use also, if I do : "Convert your ActionListener class GestionAbsenceEmployes to accept RechercherAbsenceP3 as the parameter so we can call getAjouter() on its instance like so:" Then only the panel 3 is accessible. How would I get the other 2 panels ? – Mobidoy Dec 16 '12 at 21:55
2

Here is how I would do it. Make GestionAbsenceEmployes a non-static inner class of CreerRechercherAbsence

public class CreerRechercherAbsence extends JFrame {

    private GestionAbsenceEmployes aControleur;
    private JPanel absenceP1, absenceP2;
    private RechercherAbsenceP3 absenceP3;

    // code omitted
    public CreerRechercherAbsence() {
        super("Gestionnaire des employés absents");

        try {
            jbInit();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        aControleur = new GestionAbsenceEmployes(); 
    }

    // code omitted
    class GestionAbsenceEmployes implements ActionListener{

        private AbsenceEmploye modele;

        public GestionAbsenceEmployes() {
            this.modele = new AbsenceEmploye();
        }

        public AbsenceEmploye getModele() {
            return modele;
        }


        @Override
        public void actionPerformed(ActionEvent event) {
            Object source = event.getSource();
            if(source == absenceP3.getAjouter()) {

            }
        }
    }

No need to pass this to the constructor and the controller does not need a reference to vue. You get all that for free by making this an inner class. Your controller can access all the member variables of the view. So you can now access the absenseP3 panel with the getAjouter() method.

See http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html for more information on when it makes sense to use inner classes.

Guido Simone
  • 7,912
  • 2
  • 19
  • 21
  • if(source == absenseP3.getAjouter()) This live gives me an error saying that absenceP3 cannot be resolved (corrected a typo in absenseP3 to absenceP3) – Mobidoy Dec 16 '12 at 22:35
  • Thanks. Fixed typo plus subsequent compile error. In order to use this solution, you need to understand inner classes. Once you make GestionAbsenceEmploys an inner class of CreerRechercherAbsence, all the code in GestionAbsenceEmployes will be able to access all private variables of CreerRechercherAbsence. Hint - you will need to delete the file GestionAbsenceEmploys.java. – Guido Simone Dec 16 '12 at 23:55
  • Dang, this cannot work then, that file (GestionAbsenceEmployes.java) is requested by the teacher. We can have has many files we want for the gui's in the ca.uqam.inf2120.tp2.interfacegraphique package but, the controler has to be in GestionAbsenceEmployes and the model in AbsenceEmployes both in the ca.uqam.inf2120.tp2.modele. – Mobidoy Dec 17 '12 at 01:21
2

Not sure whether the following approach will be considered MVC, or whether it will result in good marks on your assignment.

My "problem" with your current approach is that the reusability is limited to the model, and that it looks difficult to write a decent test case for this code, unless you are prepared to write tests which include the whole view.

When I need to write a Swing application, it seems that I only end up with 2 classes: a model class defining the data and the operations available on that data, and the view class. The view class functions both as view as well as controller. When I have a button as in your example, I would attach an ActionListener to it (or use an Action) which just retrieves the necessary information from the view without any logic. It passes all that information directly to the model side where all the logic is located.

The two main benefits I see in this approach:

  • I can re-design my view without any problems. If I decide to remove a JButton and provide the user with another mechanism for that same operation, all my changes are limited to the view. I have no dependency on UI elements except in my view class. I see all the "information gathering and passing it to the model" directly in my view class, and due to the implementation of that view this will not affect other classes. Compare that with your code where you have a source == vue.getAjouter() check in a class outside your view.
  • I can test the model and all its logic without needing my actual view. So I can skip the whole "firing up a Swing UI" in a unit test and still test all my logic. If I want to test the UI (for example to test whether a certain button is disabled when a field is left blank) I can test this separately in an integration test (as having a UI tends to slow down your tests).

What I found a very interesting article in this regard is The humble dialog box

Robin
  • 36,233
  • 5
  • 47
  • 99