I am having a problem with hidden components not being disposed properly when working with multiple frames.
In short, I cannot dispose a modal dialog whose parent is a hidden frame.
For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class MultipleFrameTest {
public static void main(String[] args) {
TestFrame test = new TestFrame();
FrameTester tester = new FrameTester(test);
tester.setVisible(true);
}
private static class TestFrame extends JFrame {
JDialog dialog;
java.util.Timer timer;
public TestFrame() {
super("Test Frame");
this.dialog = null;
this.timer = new java.util.Timer("Frame Timer");
fillFrame();
pack();
}
private void fillFrame() {
JButton dialogButton = new JButton("Launch Model Dialog");
dialogButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
JOptionPane pane = new JOptionPane("Wait for 2 seconds",
JOptionPane.QUESTION_MESSAGE,
JOptionPane.OK_CANCEL_OPTION);
dialog = pane.createDialog(TestFrame.this, "Question");
timer.schedule(new TimerTask() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
TestFrame.this.setVisible(false);
if (dialog != null) {
dialog.setVisible(false);
dialog.dispose();
dialog = null;
}
}
});
}
}, 2 * 1000);
dialog.setVisible(true);
}
});
JPanel panel = new JPanel();
panel.add(dialogButton);
add(panel);
}
}
private static class FrameTester extends JFrame {
JFrame frame;
public FrameTester(JFrame frame) {
super("Frame Tester");
this.frame = frame;
fillFrame();
pack();
}
private void fillFrame() {
JButton toggleButton = new JButton("Toggle Frame Visibility");
toggleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
frame.setVisible(!frame.isVisible());
}
});
JPanel panel = new JPanel();
panel.add(toggleButton);
add(panel);
}
}
}
To run this example:
TestFrame
.JOptionPane
.TimerTask
to hide the TestFrame
and dispose()
the JOptionPane
.TestFrame
will become visible and the JOptionPane
will see be attached.I know that I can fix this by disposing the JOptionPane
before hiding the TestFrame
:
- TestFrame.this.setVisible(false);
if (dialog != null) {
dialog.setVisible(false);
dialog.dispose();
dialog = null;
}
+ TestFrame.this.setVisible(false);
Does anybody know why this is happening? I would expect the modal dialog to be gone even if it is hidden while it is being disposed.
Although you should really consider trashgod's comment on The Use of Multiple JFrames, Good/Bad Practice?, here is the explanation on why you see this behaviour.
This happens because you hide the owning window before the dialog. When doing such thing, the owned dialog is flagged as showWithParent
and the next call to setVisible(true)
on the frame will automatically trigger the display of the owned dialog, because of that flag. The only way to avoid that, as you stated in your question, is to hide the owned dialog first, then hide the owning window.
Here is the extract of the Window.hide()
method:
synchronized(ownedWindowList) {
for (int i = 0; i < ownedWindowList.size(); i++) {
Window child = ownedWindowList.elementAt(i).get();
if ((child != null) && child.visible) {
child.hide();
child.showWithParent = true; // See here the flag set to true
}
}
}
and here the corresponding extract of the Window.show()
method:
for (int i = 0; i < ownedWindowList.size(); i++) {
Window child = ownedWindowList.elementAt(i).get();
if ((child != null) && child.showWithParent) { // Here theh flag is checked
child.show();
child.showWithParent = false; // flag is then reset
} // endif
}
Btw, instead of using this complex timer/TimerTask/invokeLater structure, you could just use javax.swing.Timer
(with setRepeats(false)
). The Swing timer always runs on the Event Dispatching Thread.