David Yee
David Yee

Reputation: 3656

Non-Terminating Window Listener From Parent Class

Can anyone explain why the following program does not terminate when closing the child window?

I have created a simple application with a parent JFrame that instantiates a child class with a JDialog. When I pass the parent reference into the child class so that the JDialog can be created with a parent (ie: new JDialog(parent);), the window listener that is added to it from the parent class causes the program to never terminate once the child window has been closed. The windows do indeed close (in terms of visibility), but the program itself still runs. The default close operation for the parent is set to JFrame.EXIT_ON_CLOSE. The default close operation for the child is set to JDialog.DISPOSE_ON_CLOSE.

If I pass no parent reference to the child class so that the JDialog is instantiated without a parent window, the example does terminate when the child window is closed. For instance, the program terminates if the following is used: ChildWindow prompt = new ChildWindow(null);

Parent Class:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ParentWindow {
    private JFrame frame;
    private JPanel contentPane = new JPanel(new BorderLayout(0, 0));
    private JButton btnNewButton = new JButton("Open Child Window!");

    public ParentWindow() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);

        btnNewButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

                prompt.getDialog().addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosed(WindowEvent e){
                        // .. get some information from the child before disposing 
                        System.out.println("Window closed."); // does not terminate when passing frame as parent
                        frame.dispose();
                    }
                });
            }
        });

        contentPane.add(btnNewButton);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    new ParentWindow();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Child Class:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ChildWindow {
    private JDialog dialog;
    private JButton okButton = new JButton("Close");

    public ChildWindow(Window parent) {
        dialog = new JDialog(parent);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        dialog.getContentPane().setLayout(new BorderLayout());
        dialog.getContentPane().add(okButton, BorderLayout.CENTER);
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dialog.dispose();
            }
        });

        dialog.pack();
        dialog.setVisible(true);
    }

    public JDialog getDialog() {
        return dialog;
    }

}

Output:

Window closed.
Window closed.
Window closed.
... (does not terminate)

Upvotes: 1

Views: 748

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347314

Tested using Windows 7 and Java 8 and had not issues, when I tried Java 7 or Java 6, it simply kept on printing out Window Closed....

So, I updated the windowClosed method using a test for frame.isVisible

btnNewButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

        prompt.getDialog().addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                // .. get some information from the child before disposing 
                System.out.println("Window closed."); // does not terminate when passing frame as parent
                if (frame.isVisible()) {
                    frame.dispose();
                }
            }
        });
    }
});

And got it to work...now to see if I can find out why...

I "think" what's happening is this...

  1. okButton is triggered, dialog.dispose is called
  2. dialog.dispose is setting up the first windowClosed event
  3. The WindowListener is been notified of the windowClosed event, which is disposing of the frame...
  4. The frame is trying to close it's child windows, which is causing the dialogs to notify the WindowListener AGAIN that they are closing and so forth...goto 3 and repeat...

This is a bug, which appears to be fixed in Java 8...Because the closing events are been pushed onto the Event Queue, you won't get a StackOverflowException...

In this case, another option would be to remove the WindowListener from the Window that triggered the windowClosed event...

btnNewButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

        prompt.getDialog().addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                e.getWindow().removeWindowListener(this);
                frame.dispose();
            }
        });
    }
});

Upvotes: 3

Related Questions