Creating a custom dialog box by extending JDialog

Although there are several dialog boxes designed for
simple tasks (such as the ones provided by
JOptionPane), sometimes you need to create a custom
dialog box to gather input. The way to
do this, is to extend the JDialog class.
Here is a class that illustrates a way
to do that:


import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JComboBox;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Insets;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Frame;
import java.awt.Point;
public class MyDialog extends JDialog implements ActionListener {
   private String[] data;
   private JTextField descBox;
   private JComboBox<String> colorList;
   private JButton btnOk;
   private JButton btnCancel;
   public MyDialog(Frame parent) {
      super(parent,"Enter data",true);
      Point loc = parent.getLocation();
      setLocation(loc.x+80,loc.y+80);
      data = new String[2]; // set to amount of data items
      JPanel panel = new JPanel();
      panel.setLayout(new GridBagLayout());
      GridBagConstraints gbc = new GridBagConstraints();
      gbc.insets = new Insets(2,2,2,2);
      JLabel descLabel = new JLabel("Description:");
      gbc.fill = GridBagConstraints.HORIZONTAL;
      gbc.gridx = 0;
      gbc.gridy = 0;
      panel.add(descLabel,gbc);
      descBox = new JTextField(30);
      gbc.gridwidth = 2;
      gbc.gridx = 1;
      gbc.gridy = 0;
      panel.add(descBox,gbc);
      JLabel colorLabel = new JLabel("Choose color:");
      gbc.gridwidth = 1;
      gbc.gridx = 0;
      gbc.gridy = 1;
      panel.add(colorLabel,gbc);
      String[] colorStrings = {"red","yellow","orange","green","blue"};
      colorList = new JComboBox<String>(colorStrings);
      gbc.gridwidth = 1;
      gbc.gridx = 1;
      gbc.gridy = 1;
      panel.add(colorList,gbc);
      JLabel spacer = new JLabel(" ");
      gbc.gridx = 0;
      gbc.gridy = 2;
      panel.add(spacer,gbc);
      btnOk = new JButton("Ok");
      btnOk.addActionListener(this);
      gbc.gridwidth = 1;
      gbc.gridx = 0;
      gbc.gridy = 3;
      panel.add(btnOk,gbc);
      btnCancel = new JButton("Cancel");
      btnCancel.addActionListener(this);
      gbc.gridx = 1;
      gbc.gridy = 3;
      panel.add(btnCancel,gbc);
      getContentPane().add(panel);
      pack();
   }
   public void actionPerformed(ActionEvent ae) {
      Object source = ae.getSource();
      if (source == btnOk) {
         data[0] = descBox.getText();
         data[1] = (String)colorList.getSelectedItem();
      }
      else {
         data[0] = null;
      }
      dispose();
   }
   public String[] run() {
      this.setVisible(true);
      return data;
   }
}

Lines 1-13 import the various classes used for
this dialog box. On line 14, we see
that this class inherits from the JDialog class
and implements the ActionListener interface. We need to
inherit from the JDialog class, as the JDialog
class has a constructor that will allow the
creation of a modal dialog box.

✴ ✴ ✴ ✴ ✴

A modal dialog box, is a dialog box
that must be dealt with before the program
can continue. In a desktop application, dialog boxes
are nearly always modal dialog boxes. This forces
the user to deal with the dialog box
before continuing. In contrast, modal dialog boxes should
not be used in a web application because
it is easy for the dialog box to
wind up behind another window. If a modal
dialog box got hidden behind a window, it
would prevent the program (that the modal dialog
box is associated with) from continuing. This would
make it look like the program or the
computer has locked up.

✴ ✴ ✴ ✴ ✴

Implementing the ActionListener interface allows this application to
respond to ActionEvents. We can set our buttons
(the Ok button and the Cancel button) to
trigger ActionEvents when they are pressed.

Lines 15-19 define instance variables that must be
accessed from different parts of this class. Some
variables (like the JLabel and JPanel variables) only
need to be visible inside the constructor, so
those variables are declared inside the constructor. But,
the variables on lines 15-19 need to be
accessed from either the actionPerformed() method or the
run() method, so they are declared outside the
constructor.

Lines 20-67 define the constructor for this class.
Most of the constructor consists of instructions that
create the contents of this dialog box. On
line 21, the constructor starts by calling a
constructor from the JDialog (parent) class.


super(parent,"Enter data",true);

The first argument specifies the component that this
dialog box will treat as its parent. This
is the component that the dialog box cannot
be hidden behind. In the calling program, we
will choose this parent component to be the
main application window. Note that this parent component
must be declared as a java.awt.Frame object, not
a javax.swing.JFrame object. This is because a Frame
is heavyweight object, while a JFrame is a
lightweight object. Trying to construct a dialog box
with a JFrame parent would cause a runtime
error.

Lines 22 and 23 are used to help
set the location of the dialog box relative
to the parent window. Line 22 gets the
location of the upper left corner of the
parent window. Line 23 will set the upper
left corner of the dialog box to be
80 pixels to the right, and 80 pixels
below the upper left corner of the parent
window.

Line 24 constructs the String array to hold
2 values. In this example, we need to
get two input values. Increase this value if
you need to input more than 2 values.

Line 25 constructs a JPanel object. When we
construct the components that make up the prompts
and input areas for the dialog box, these
components will be added to this JPanel. (Then
the JPanel will be added to the Content
Pane of the dialog box window.)

On line 26, we set the layout to
be a GridBagLayout. The GridBagLayout is one of
the more complex layouts, but it is also
one of the more flexible layouts. We will
use only a few of the properties of
the GridBagLayout, but this is what makes the
constructor have a lot of lines of code.
One line 27, we construct a GridBagConstraints object.
The GridBagConstraints object is used to control how
the components are laid out in the GridBagLayout.
The GridBagLayout, lays out the components in a
grid with and columns.

On line 28, we construct the Insets object.
The Insets object specifies the space (in pixels)
a container must leave at each of its
edges. The order of the edges is:


Insets(top, left, bottom, right)

In this example, we are setting all of
the spaces at the edges to be 2
pixels.

Lines 29-33 are used to add a JLabel
to the JPanel object. Line 29 constructs this
label so that it will have the text,
“Description:”. Line 30 makes the columns stretch horizontally
(but not vertically) to fill the window. (This
will be applied to all the columns in
this example.) Lines 31 and 32 specify the
grid position for the label. Finally on line
33:


panel.add(descLabel,gbc);

will add the titleLabel component using the constraints
set in the GridBagConstraints object called gbc.

Lines 34-38 add the JTextField object called descBox
to the JPanel. Note that descBox is declared
outside of the constructor (on line 16), so
that descBox can be seen inside other methods,
such as seen on line 71. On line
35 we set the gridwidth property to 2,
so that this JTextField object will span two
columns. This is an important step in controlling
the appearance of this dialog box. Lines 36
and 37 set the location to be column
1, row 0. Finally, the component is added
on line 38.

Lines 39-43 create and add a JLabel to
prompt for the a color to be chosen.
Note that the gridwidth is set back to
1, so this JLabel spans a single column.

Lines 44-49 create and add a JComboBox<String> object
that holds the drop-down list that allows the
selecting of a color. Line 44 creates and
array of Strings that contains the choices for
the drop down list. The JComboBox<String> component has
a gridwidth of 1, and so it spans
a single column. This will keep make the
JComboBox<String> object more narrow than the JTextField objects
(as the JTextField objects span 2 columns).

Lines 50-53 add a blank row before the
buttons are displayed. On line 50, note that
the JLabel is defined with a space (and
is not an empty String). Using an empty
String would cause this row to collapse so
that the blank row effect would not materialize.

Lines 54-59 create and add JButton object for
the Ok button. Note on line 55, that
we make this dialog box listen for the
ActionEvent that will result when the Ok button
is hit.

Lines 60-64 create and add a JButton object
for the Cancel button. Note on line 61,
we make this dialog box object listen for
the ActionEvent that will result when the Cancel
button is hit.

On line 65, the JPanel with all the
components is added to the Content Pane of
the dialog box. One line 66, the pack()
method is used to clean up the layout
of all the components in the dialog box.

When this dialog box is displayed it looks
like this:

Note, how the JComboBox has a column span
of 1, whereas the JTextField object spans 2
columns.

Lines 68-78 define the actionPerformed() method. This is
the method that is called whenever an ActionEvent
is generated for this dialog box. Line 69
gets the Object that is the originating source
for the ActionEvent message. On lines 70- 73,
an if statement that handles the case of
hitting the Ok button is defined. Note on
line 70, how the == operator is used,
as we are really testing to see if
the source is the same object as the
Ok button. If the Ok button has been
hit, we store the input values in the
data array. Note on line 71 how the
JTextField object needs to be accessed from this
method. That is why that object was declared
before the constructor (on line 16). On line
72, we get the JComboBox<String> (named colorList) and
get the selected item. The getSelectedItem() method returns
a java.lang.Object, so we cast this to a
String on line 72.

Note on lines 74-76, we define an else
statement that just sets the first element in
data to be null. We use an else
statement instead of trying to match the Cancel
button. This allows us to do the same
thing whether the user hits the Cancel button,
or the close button in the title bar
of the dialog box.

One line 77, the dialog box is closed
and cleared from memory.

Lines 79-82 define an important method called run().
The run() method will be used to make
the already constructed dialog box visible. In addition,
when the user hits the Ok button, Cancel
button, or the close button, the run() method
will return the data to the calling application.

To demonstrate this dialog box, a simple application
class called TestDialog is created:


import javax.swing.*;
import java.awt.event.*;
class TestDialog extends JFrame implements ActionListener {
   public TestDialog(String title) {
      super(title);
      setBounds(0,0,600,400);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      JMenuBar menuBar = new JMenuBar();
      setJMenuBar(menuBar);
      JMenu file = new JMenu("File");
      JMenuItem quit = new JMenuItem("Quit");
      quit.addActionListener(this);
      file.add(quit);
      menuBar.add(file);
      JMenu data = new JMenu("Data");
      JMenuItem enter = new JMenuItem("Enter data");
      enter.addActionListener(this);
      data.add(enter);
      menuBar.add(data);
   }
   public void actionPerformed(ActionEvent ae) {
      String choice = ae.getActionCommand();
      if (choice.equals("Quit")) {
         System.exit(0);
      }
      else if (choice.equals("Enter data")) {
         MyDialog dlg = new MyDialog(this);
         String[] results = dlg.run();
         if (results[0] != null) {
            JOptionPane.showMessageDialog(this,
               results[0] + ", color: " + results[1]);
         }
      }
   }
   public static void main(String[] args) {
      TestDialog myApp = new TestDialog("Test Dialog");
      myApp.setVisible(true);
   }
}

Most of the code here is just used
to create an application with menus. The important
lines are lines 26-33. Line 27 constructs the
MyDialog object using the application as the parent
window. Note that the parent window is a
javax.swing.JFrame object. Recall that the constructor for MyDialog
called for a java.awt.Frame object. The reason why
this call to the constructor works, is that
javax.swing.JFrame extends java.awt.Frame. This means that a JFrame
object is a Frame object.

On line 28 we set a String array
called results to hold the array returned by
the run() method. On lines 29-32, we display
a message box that contains the results if
the first element in the returned array is
not null. (Recall that if the user hits
the Cancel button or the close button, the
first element will be set to null.)