Question

I've created a Swing application with several JInternalFrames which gets added to a JDesktopPane on the event of a mouse click. I want only one instance of the same Internal frame to be present on the DesktopPane. I dont want the same frame to appear twice when the user opens the frame. If the frame is already open, then a error message should appear.!

Thanks a lot :)

Was it helpful?

Solution

Don't bother with a singleton anti-pattern. Instead simply give your class a JInternalFrame field and create one instance of your JInternalFrame in your class's constructor or at variable declaration, and don't create a new one on mouse click, but rather display the one that has been already created. For instance in the mousePressed method, simply call myInternalFrame.setVisible(true). This way, if it was invisible, now it's visible, and if it's already visible, then it's still visible and unchanged. Simple and straightforward.

OTHER TIPS

I've created a Swing application with several JInternalFrames...

and

I want only one instance of the same Internal frame...

So apply the Singleton Pattern to each of your JInternalFrame child classes. You will be able to use only one instance of a class if the class is conforming to the singleton pattern.

HovercraftFullOfEeels, a man after my own heart, says not to use the Singleton pattern, and I'm going to disagree. Singleton can be a very powerful way to streamline things and avoid boilerplate code, while keeping the system strong and easy to maintain. Further, his suggestion that you simply display an already-opened JInternalFrame is flawed in two ways: 1) That is difficult to manage on the code level and leaves your system fragile and difficult to update in the future and 2) When end-users close and reopen a screen, they expect a new instance, with refreshed data and brand new components. It is not expected that closing and reopening would be the same instances.

The other answerer says to use Singleton, but gives no concrete example. So, I'm going to give you the code I've developed for my application:

This is the class for the Singleton JInternalFrame (note: the reason it extends JPanel is so that I can easily use it in a GUI Builder):

public abstract class VPanel extends JPanel {

    public static JDesktopPane desktopPane;

    public static void installDesktopPane(JDesktopPane desktopPane) {
        VPanel.desktopPane = desktopPane;
    }

    public VPanel(String name) {
        this.name = name;
        if(desktopPane == null)
            throw new IllegalStateException("VPanel is being used with a null desktop pane.");
    }
    static LinkedHashMap<Class, VPanel> self_panel_map;

    JInternalFrame self_jif;
    protected VPanel self_panel;
    boolean loading;
    boolean showing;

    public final String name;
    public abstract void init();

    public static VPanel showPanel(VPanel newInstance) {
        if(self_panel_map == null)
            self_panel_map = new LinkedHashMap<>();
        Class newInstanceClass = newInstance.getClass();
        if(self_panel_map.containsKey(newInstanceClass)) {
            VPanel oldInstance = self_panel_map.get(newInstanceClass);
            oldInstance.showing = oldInstance.self_jif.isVisible();
            if(!oldInstance.loading && !oldInstance.showing) {
                newInstance.loading = true;
                newInstance.self_panel = newInstance;
                newInstance.self_jif = new JInternalFrame(newInstance.name, true, true, true, true);
                newInstance.self_panel.init();
                self_panel_map.put(newInstanceClass, newInstance);
                return newInstance;
            } else if(oldInstance.showing) {
                try {
                    oldInstance.self_jif.setSelected(true);
                } catch (PropertyVetoException e) {
                    handleError(e);
                }
            }
            return oldInstance;
        } else {
            newInstance.loading = true;
            newInstance.self_panel = newInstance;
            newInstance.self_jif = new JInternalFrame(newInstance.name, true, true, true, true);
            newInstance.self_panel.init();
            self_panel_map.put(newInstanceClass, newInstance);
            return newInstance;
        }
    }

    public void setVisible() {

        self_jif.add(self_panel);
        self_jif.pack();
        self_jif.setVisible(true);
        desktopPane.add(self_jif);
        centerJIF();
        try {
            self_jif.setSelected(true);
        } catch (PropertyVetoException e) {
            handleError(e);
        }
        loading = false;
    }

    private static void handleError(Exception e) {
        e.printStackTrace();
    }

    public void centerJIF() {
        Dimension desktopSize = desktopPane.getSize();
        Dimension JInternalFrameSize = self_jif.getSize();
        int width = (desktopSize.width - JInternalFrameSize.width) / 2;
        int height = (desktopSize.height - JInternalFrameSize.height) / 2;
        self_jif.setLocation(width, height);
    }
}

Here is a sample code that would implement it:

public static void main(String[] args) {
    JFrame jf = new JFrame("MainFrame");
    JDesktopPane jdp = new JDesktopPane();
    jf.setExtendedState( jf.getExtendedState()|JFrame.MAXIMIZED_BOTH );

    VPanel.installDesktopPane(jdp); // This only needs to happen once throughout the entire application lifecycle.

    JMenuBar menuBar = new JMenuBar();
    JMenu menu = new JMenu("Panels");
    JMenuItem menuItem = new JMenuItem("Open Test Panel");
    menuItem.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            Test_VPanel.showPanel(new Test_VPanel()); // Every time you show the panel, you create a new instance.
            // But this new instance is only used if it is needed. The init() method is only called if it is going
            // To show a new instance.
        }
    });
    menu.add(menuItem);
    menuBar.add(menu);
    jf.setJMenuBar(menuBar);


    jf.setContentPane(jdp);

    jf.setVisible(true);
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

static class Test_VPanel extends VPanel {

    public Test_VPanel() {
        super("Test Panel");
    }

    @Override
    public void init() {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        JLabel label = new JLabel("JLabel");
        JTextField textField = new JTextField();

        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1;
        gbc.gridwidth = 1;
        gbc.gridy = 0;
        gbc.insets = new Insets(4,4,4,4);
        add(label, gbc);

        gbc.gridy = 1;
        add(textField, gbc);

        setVisible(); // This needs to be called at the end of init()
    }
}

I never need the calling method to do anything with the new instance, but just incase, showPanel returns the instance used, whether it's the old instance or the new instance. So, if you need to do something with the instance you would do this:

Test_VPanel panel = Test_VPanel.showPanel(new Test_VPanel());
panel.something();
...

Life has been a lot easier for me once I decided to go this route with Singleton JIFs. Highly recommended.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top