Using JTables with JComboBox Cell Editors in Frame-based Applications

This JTable has its own TableModel which sets the third and fourth columns only to be editable. Their editor is a JComboBox, as shown in the screenshot.

You can move the code of the MyTableModel class (keeping the same namespace) to a different .pas file and add its reference to the project file. We decided to keep the classes in the same file because they are fairly short.

Combo box cell editor in action

Combo box cell editor in action

You can compare this code with the RemObjects C# version below it.

The code in table_app2.pas

namespace table_app2;

interface

uses
  java.util, java.awt.*, javax.swing.*;

type
  TableDemo = public class(JFrame, TableModelListener)
  private
    lblChangedValue : JLabel;
    tblGrades : JTable;
    verticalBox : Box;
    sp : JScrollPane;
    model : MyTableModel;
    cbo : JComboBox;
    editor : TableCellEditor;
  public
    constructor;
    class method Main(args : array of String);
    method tableChanged(e : TableModelEvent);
  end;

type
  MyTableModel = class(AbstractTableModel)
  private
    columnNames : array[0 .. 3] of String;
    data : array[0 .. 4] of array[0 .. 3] of Object;

  public
    constructor;
    method getColumnCount : Integer;
    method getRowCount : Integer;
    method getColumnName(col : Integer) : String; override;
    method getValueAt(row, col : Integer)  : Object;
    method isCellEditable(row,  col : Integer) : Boolean; override;
    method setValueAt(value : Object; row, col : Integer); override;
  end;

implementation

constructor TableDemo;
begin
  model := new MyTableModel;
  verticalBox := Box.createVerticalBox;
  tblGrades := new JTable(model);
  tblGrades.getColumnModel.getColumn(0).setPreferredWidth(100);
  tblGrades.getColumnModel.getColumn(1).setPreferredWidth(100);
  tblGrades.getColumnModel.getColumn(2).setPreferredWidth(80);
  tblGrades.getColumnModel.getColumn(3).setPreferredWidth(80);
  tblGrades.getTableHeader.setReorderingAllowed(false);
  model.addTableModelListener(self);

  cbo := new JComboBox;
  cbo.addItem('Pass');
  cbo.addItem('Fail');
  cbo.addItem('Distinction');
  cbo.setSelectedItem('Distinction');
  cbo.setMaximumRowCount(4);
  editor := new DefaultCellEditor(cbo);
  tblGrades.getColumnModel.getColumn(2).setCellEditor(editor);
  tblGrades.getColumnModel.getColumn(3).setCellEditor(editor);

  sp := new JScrollPane(tblGrades);
  verticalBox.add(sp);

  lblChangedValue := new JLabel('Changed value: ');
  verticalBox.add(lblChangedValue);
  add(verticalBox);

  //Ensure app closes when you click the close button
  DefaultCloseOperation := JFrame.EXIT_ON_CLOSE;
  //Set window caption and size
  Title := 'Table Demo 2';
  Size := new Dimension(400, 150);
  setVisible(true);
end;

class method TableDemo.Main(args : array of String);
begin
  new TableDemo;
end;

method TableDemo.tableChanged(e : TableModelEvent);
var
  strChanged : String;
begin
  if e.getType = TableModelEvent.UPDATE then
    begin
      strChanged := 'Changed value: ' + tblGrades.getValueAt(e.getFirstRow, e.getColumn).toString;
      lblChangedValue.setText(strChanged);
    end;
end;

constructor MyTableModel;
begin
 // inherited;
  columnNames := ['Forename', 'Surname',  'Theory', 'Practical'];
  data := [['Jo', 'Wood', 'Pass', 'Distinction'],
           ['John', 'Bode', 'Fail', 'Pass'],
           ['Kapil', 'Shah', 'Pass', 'Pass'],
           ['Kathy', 'Parr', 'Distinction', 'Pass'],
           ['Helen', 'Coe', 'Pass', 'Fail']];
end;

method MyTableModel.getColumnCount : Integer;
begin
  result := columnNames.length;
end;

method MyTableModel.getRowCount : Integer;
begin
  result := data.length;
end;

method MyTableModel.getColumnName(col : Integer) : String;
begin
  result := columnNames[col];
end;

method MyTableModel.getValueAt(row, col : Integer)  : Object;
begin
  result := data[row][col];
end;

method MyTableModel.isCellEditable(row,  col : Integer) : Boolean;
begin
  if col > 1 then
    result := true
  else
    result := false;
end;

method MyTableModel.setValueAt(value : Object; row, col : Integer);
begin
  data[row][col] := value;
  fireTableCellUpdated(row, col);
end;

end.

RemObjects C# Version of Table Application

using java.awt;
using javax.swing;
using javax.swing.table;
using javax.swing.@event;

namespace table_app_cs
{
  public class TableDemo : JFrame, TableModelListener
  {
    private JLabel lblChangedValue;
    private JTable tblGrades;
    private Box verticalBox;
    private JScrollPane sp;
    private MyTableModel model;
    private JComboBox cbo;
    private TableCellEditor editor;

    public TableDemo()
    {
      model = new MyTableModel();
      verticalBox = Box.createVerticalBox();
      tblGrades = new JTable(model);
      tblGrades.getColumnModel().getColumn(0).setPreferredWidth(100);
      tblGrades.getColumnModel().getColumn(1).setPreferredWidth(100);
      tblGrades.getColumnModel().getColumn(2).setPreferredWidth(80);
      tblGrades.getColumnModel().getColumn(3).setPreferredWidth(80);
      tblGrades.getTableHeader().setReorderingAllowed(false);
      model.addTableModelListener(this);

      cbo = new JComboBox();
      cbo.addItem("Pass");
      cbo.addItem("Fail");
      cbo.addItem("Distinction");
      cbo.setSelectedItem("Distinction");
      cbo.setMaximumRowCount(4);
      editor = new DefaultCellEditor(cbo);
      tblGrades.getColumnModel().getColumn(2).setCellEditor(editor);
      tblGrades.getColumnModel().getColumn(3).setCellEditor(editor);

      sp = new JScrollPane(tblGrades);
      verticalBox.add(sp);

      lblChangedValue = new JLabel("Changed value: ");
      verticalBox.add(lblChangedValue);
      add(verticalBox);

      //Ensure app closes when you click the close button
      DefaultCloseOperation = JFrame.EXIT_ON_CLOSE;
      //Set window caption and size
      Title = "Table Demo";
      Size = new Dimension(400, 150);
      setVisible(true);
    }

    public static void Main(string[] args)
    {
      new TableDemo();    
    }

    public void tableChanged(TableModelEvent e)
    {
      String strChanged;
      if (e.getType() == TableModelEvent.UPDATE)
      {
        strChanged = "Changed value: " + tblGrades.getValueAt(e.getFirstRow(), e.getColumn()).toString();
        lblChangedValue.setText(strChanged);
      }
    }
  }

  public class MyTableModel: AbstractTableModel
  {
    private String[] columnNames = new String[3];
    Object[][] data = new Object[4][];

    public MyTableModel()
    {
      columnNames = {"Forename", "Surname",  "Theory", "Practical"};
      data = {{"Jo", "Wood", "Pass", "Distinction"},
               {"John", "Bode", "Fail", "Pass"},
               {"Kapil", "Shah", "Pass", "Pass"},
               {"Kathy", "Parr", "Distinction", "Pass"},
               {"Helen", "Coe", "Pass", "Fail"}};
    }

    public Integer getColumnCount()
    {
      return columnNames.length;
    }

    public Integer getRowCount()
    {
      return data.length;
    }

    public override string getColumnName(Integer col) 
    {
      return columnNames[col];
    }

    public Object getValueAt(Integer row, Integer col)
    {
    return data[row][col];
    }

    public override Boolean isCellEditable(Integer row,  Integer col)
    {
    if (col > 1) 
      return true;
    else
      return false;
    }

    public override void setValueAt( Object value,  Integer row, Integer col)
    {
      data[row][col] = value;
      fireTableCellUpdated(row, col);
    }
  } 
}


Programming - a skill for life!

Introduction to the use of JTables in frame-based applications in Oxygene for Java