# JTable: Einzelne Zelle selektieren



## jacquipre79 (7. Jul 2008)

Hallo!

Ich suche nun schon seit Tagen nach einer Lösung für mein Problem. Ich habe auch schon dieses Forum "durchforstet", konnte aber noch keine passende Antwort finden. Vielleicht könnt ihr mir weiterhelfen...

Das Problem:
Ich habe eine Tabelle mit 2 Spalten und 2 Zeilen. In dieser Tabelle kann man in einer Zelle einen Text eingeben. Wenn die Enter-Taste betätigt wird, überprüfe ich den Inhalt der Zelle. Hat dieser spezielle, von mir nicht erlaubte Sonderzeichen, dann öffne ich ein kleines Dialogfenster mit einem Hinweis dazu. Schließt man das Dialogfenster wieder, soll die zuvor verwendete Tabellenzelle wieder selektiert sein und der Textcursor (Caret) soll zu sehen sein. Das bekomme ich nicht hin.

Hier mal mein Aufbau:

Mein Editor ist ein JPanel und enthält die JTable:

```
public class MyEditor extends javax.swing.JPanel {
...
private javax.swing.JTable innerTable; // Tabelle
private MyEditor.Model myModel = new MyEditor.Model(this); // TableModel
private MyEditor.CellRenderer myRenderer = new MyEditor.CellRenderer(this); // CellRenderer
private final MyCellEditor myEditor = new MyCellEditor(this); // CellEditor

public MyEditor() {
       ...
        initComponents();   
        this.innerTable.setDefaultRenderer(String.class, this.myRenderer);
        this.innerTable.setDefaultEditor(String.class, this.myEditor);   
        
        ...
        this.innerTable.setSurrendersFocusOnKeystroke(true);
}

private void initComponents() {
        innerTable = new javax.swing.JTable();
        ...
        innerTable.setModel(this.myModel);
        innerTable.setOpaque(false);
        innerTable.setSurrendersFocusOnKeystroke(true);
        innerTable.setVerifyInputWhenFocusTarget(false);
        innerTable.setCellEditor(myEditor);
        
        add(innerTable, java.awt.BorderLayout.CENTER);
        ...
}
```

Zudem hat diese Klasse noch eine setValueAt-Methode, in der ich auch die Überprüfung des Textes vornehme:

```
private void setPropertyValueAt(int index, int columnIndex, Object value) {
       if (!value.toString().matches("[a-zA-Z0-9_]*"))
       {
            javax.swing.JOptionPane.showConfirmDialog(null,"No special characters allowed", "Error",
        				javax.swing.JOptionPane.DEFAULT_OPTION,
        				javax.swing.JOptionPane.WARNING_MESSAGE);
           
            // ======= Hier jetzt die letzte Zelle wieder selektieren ==========
            // probiert bis jetzt:
            // this.innerTable.changeSelection(index, columnIndex, false, false); // hebt die Zelle farblich hervor, setzt aber nicht den Textcursor
            // this.innerTable.editCellAt( index, columnIndex); // ich bin in einer Endlosschleife, d.h. der Fehlerdialog erscheint ständig



        	
        }
        
        ...       
}
```

Es gibt auch ein eigenes Table Model innerhalb dieser Klasse:

```
class Model extends AbstractTableModel {
        private static final long serialVersionUID = 1L;
        MyEditor myOwner;
        
        
        private Model(MyEditor pOwner) {
            this.myOwner = pOwner;
        }
    
        public int getColumnCount() {
            return 2;
        }
        
        public int getRowCount() {
            return this.myOwner.myKeys.length;
        }
        
        public Object getValueAt(int rowIndex, int columnIndex) {
            return (columnIndex == 0 ? this.myOwner.getPropertyNameAt(rowIndex) : this.myOwner.getPropertyValueAt(rowIndex));
        }   
        
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return (columnIndex == 1);
        }
                
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {	
            this.myOwner.setPropertyValueAt(rowIndex, columnIndex, aValue);
        }        
        
        public Class<String> getColumnClass(int columnIndex) {
            return String.class;
        }
    }
```

Ich hoffe, das ist jetzt verständlich!

-- jacquipre


----------



## André Uhres (7. Jul 2008)

Versuch mal die Fehlerbehandlung in den MyCellEditor zu verlegen, und zwar in diese Methode:

```
@Override
    public boolean stopCellEditing() {
        String value = ((JTextField) getComponent()).getText();
        if (!value.matches("[a-zA-Z0-9_]*")) {
            JOptionPane.showConfirmDialog(null, "No special characters allowed", "Error",
                    JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
            return false;
        }
        return super.stopCellEditing();
    }
```

So funktioniert es bei mir:

```
package table;
/*
 * MyEditor.java
 */

import javax.swing.*;
import javax.swing.table.*;

public class MyEditor extends JPanel {

    private javax.swing.JTable innerTable; // Tabelle
    private MyEditor.Model myModel = new MyEditor.Model(this); // TableModel
    private MyEditor.CellRenderer myRenderer = new MyEditor.CellRenderer(this); // CellRenderer
    private final MyCellEditor myEditor = new MyCellEditor(this); // CellEditor
    private String[][] myKeys = new String[][]{{"name1", "value1"}, {"name2", "value2"}};

    public MyEditor() {
        initComponents();
        this.innerTable.setDefaultRenderer(String.class, this.myRenderer);
        this.innerTable.setDefaultEditor(String.class, this.myEditor);
        this.innerTable.setSurrendersFocusOnKeystroke(true);
    }

    private Object getPropertyNameAt(int rowIndex) {
        return myKeys[rowIndex][0];
    }

    private Object getPropertyValueAt(int rowIndex) {
        return myKeys[rowIndex][1];
    }

    private void initComponents() {
        innerTable = new JTable();
        innerTable.setModel(this.myModel);
        innerTable.setOpaque(false);
        innerTable.setSurrendersFocusOnKeystroke(true);
        innerTable.setVerifyInputWhenFocusTarget(false);
        innerTable.setCellEditor(myEditor);
        add(innerTable, java.awt.BorderLayout.CENTER);
    }

    private static class Model extends AbstractTableModel {

        private static final long serialVersionUID = 1L;
        MyEditor myOwner;

        private Model(MyEditor pOwner) {
            this.myOwner = pOwner;
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public int getRowCount() {
            return this.myOwner.myKeys.length;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return (columnIndex == 0 ? this.myOwner.getPropertyNameAt(rowIndex) : this.myOwner.getPropertyValueAt(rowIndex));
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return (columnIndex == 1);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            this.myOwner.setPropertyValueAt(rowIndex, columnIndex, aValue);
        }

        @Override
        public Class<String> getColumnClass(int columnIndex) {
            return String.class;
        }
    }

    private void setPropertyValueAt(int index, int columnIndex, Object value) {
        myKeys[index][columnIndex] = (String) value;
    }

    private static class CellRenderer extends DefaultTableCellRenderer {

        private CellRenderer(MyEditor aThis) {
        }
    }

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("MyEditor");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setSize(400, 300);
                f.add(new MyEditor());
                f.setVisible(true);
            }
        });
    }
}

class MyCellEditor extends DefaultCellEditor {
    private MyEditor myEditor;

    MyCellEditor(MyEditor myEditor) {
        super(new JTextField());
        this.myEditor = myEditor;
    }

    @Override
    public boolean stopCellEditing() {
        String value = ((JTextField) getComponent()).getText();
        if (!value.matches("[a-zA-Z0-9_]*")) {
            JOptionPane.showConfirmDialog(myEditor, "No special characters allowed", "Error",
                    JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
            return false;
        }
        return super.stopCellEditing();
    }
}
```


----------



## Guest (7. Jul 2008)

André Uhres hat gesagt.:
			
		

> Versuch mal die Fehlerbehandlung in den MyCellEditor zu verlegen...


Ok, das hat bei mir geholfen. Schon mal ein DANKE an dieser Stelle.

Ein Problem hab ich aber noch. Meine Tabellenzelle besitzt neben dem Textfeld auch noch einen Button. Klickt man auf diesen Button öffnet sich ein Dialogfenster, in dem man auch einen Text eingeben kann. Dieses Fenster hat einen OK-Button, klickt man darauf, wird es geschlossen und der Wert in das Textfeld geschrieben. Nun soll auch zu diesem Zeitpunkt die Zelle selektiert und der Textcursor sichtbar sein. Das scheint so noch nicht zu funktionieren.

Hier mal der Code. In der Datei MyCellEditor.java gibt es noch eine Action ShowComplexEditorAction, die ich dem Button als Action zugewiesen habe.

```
...
this.moreButton.setAction(new MyCellEditor.ShowComplexEditorAction(this));
...
class ShowComplexEditorAction extends AbstractAction {
    	final MyCellEditor myOwner;
        ...
        public void actionPerformed(java.awt.event.ActionEvent e) {
            
            MyComplexPropertyEditor complexEditor = new MyComplexPropertyEditor(null, true);
            
            ...
            complexEditor.setVisible(true); // anzeigen

            if (complexEditor.getCloseOperation() == MyComplexPropertyEditor.OK) 
           {
                this.myOwner.setCellEditorValue(complexEditor.getValue());
                this.myOwner.fireEditingStopped();
            } 
            else {
                this.myOwner.fireEditingCanceled();
            } 
...
}
```

Das Dialogfenster (MyComplexPropertyEditor ) ist modal. Wie kann ich den Fokus wieder auf die Tabellenzelle setzen?


----------



## André Uhres (7. Jul 2008)

Was passiert, wenn du beim Betätigen des OK-Buttons den editCellAt aufrufst, wie du's in setPropertyValueAt versucht hattest?


----------



## jacquipre79 (7. Jul 2008)

Hm... da weiß ich im Moment grad gar nicht, wie ich aus meinem MyCellEditor diese Methode editCellAt aufrufen soll. Die JTable ist ja in der MyEditor-Klasse (in der auch setPropertyValueAt ist)...


----------



## André Uhres (7. Jul 2008)

siehe Folgepost


----------



## André Uhres (7. Jul 2008)

Am besten, du zeigst uns ein kleines lauffähiges Beispiel, wo der Fehler auftritt.
Hier ist meine Version, die scheint zu funktionieren:

```
package table;
/*
 * MyEditor.java
 */

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class MyEditor extends JPanel {

    private javax.swing.JTable innerTable; // Tabelle
    private MyEditor.Model myModel = new MyEditor.Model(this); // TableModel
    private MyEditor.CellRenderer myRenderer = new MyEditor.CellRenderer(this); // CellRenderer
    private final MyCellEditor myEditor = new MyCellEditor(this); // CellEditor
    private Object[][] myKeys = new Object[][]{{new JTextField("name1"), new MyPanel("value1")}, {new JTextField("name2"), new MyPanel("value2")}};

    public MyEditor() {
        initComponents();
        this.innerTable.setDefaultRenderer(Object.class, this.myRenderer);
        this.innerTable.setDefaultEditor(Object.class, this.myEditor);
        this.innerTable.setSurrendersFocusOnKeystroke(true);
        this.innerTable.setRowHeight(22);
    }

    private Object getPropertyNameAt(int rowIndex) {
        return myKeys[rowIndex][0];
    }

    private Object getPropertyValueAt(int rowIndex) {
        return myKeys[rowIndex][1];
    }

    private void initComponents() {
        innerTable = new JTable();
        innerTable.setModel(this.myModel);
        innerTable.setOpaque(false);
        innerTable.setSurrendersFocusOnKeystroke(true);
        innerTable.setVerifyInputWhenFocusTarget(false);
        innerTable.setCellEditor(myEditor);
        add(innerTable, java.awt.BorderLayout.CENTER);
    }

    private static class Model extends AbstractTableModel {

        private static final long serialVersionUID = 1L;
        MyEditor myOwner;

        private Model(MyEditor pOwner) {
            this.myOwner = pOwner;
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public int getRowCount() {
            return this.myOwner.myKeys.length;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return (columnIndex == 0 ? this.myOwner.getPropertyNameAt(rowIndex) : this.myOwner.getPropertyValueAt(rowIndex));
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return (columnIndex == 1);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            this.myOwner.setPropertyValueAt(rowIndex, columnIndex, aValue);
        }

        @Override
        public Class<String> getColumnClass(int columnIndex) {
            return String.class;
        }
    }

    private void setPropertyValueAt(int index, int columnIndex, Object value) {
        myKeys[index][columnIndex] = value;
    }

    class CellRenderer implements TableCellRenderer {

        private MyEditor myEditor;

        private CellRenderer(MyEditor myEditor) {
            this.myEditor = myEditor;
        }

        @Override
        public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
            return (JComponent) value;
        }
    }

    public static void main(final String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("MyEditor");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setSize(400, 300);
                f.add(new MyEditor());
                f.setVisible(true);
            }
        });
    }
}

class MyCellEditor implements TableCellEditor, Serializable {

    private MyEditor myEditor;
    protected EventListenerList listenerList = new EventListenerList();
    protected transient ChangeEvent changeEvent = null;
    protected JComponent editorComponent = null;

    MyCellEditor(MyEditor myEditor) {
        this.myEditor = myEditor;
    }

    public Component getComponent() {
        return editorComponent;
    }
    /* implement CellEditor:
    getCellEditorValue 
    isCellEditable 
    shouldSelectCell 
    stopCellEditing
    cancelCellEditing 
    addCellEditorListener 
    removeCellEditorListener
     */

    @Override
    public Object getCellEditorValue() {
        return editorComponent;
    }

    @Override
    public boolean isCellEditable(final EventObject anEvent) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(final EventObject anEvent) {
        if (editorComponent != null && anEvent instanceof MouseEvent && ((MouseEvent) anEvent).getID() == MouseEvent.MOUSE_PRESSED) {
            Component dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent, 3, 3);
            MouseEvent e = (MouseEvent) anEvent;
            MouseEvent e2 = new MouseEvent(dispatchComponent, MouseEvent.MOUSE_RELEASED, e.getWhen() + 100000, e.getModifiers(), 3, 3, e.getClickCount(), e.isPopupTrigger());
            dispatchComponent.dispatchEvent(e2);
            e2 = new MouseEvent(dispatchComponent, MouseEvent.MOUSE_CLICKED, e.getWhen() + 100001, e.getModifiers(), 3, 3, 1, e.isPopupTrigger());
            dispatchComponent.dispatchEvent(e2);
        }
        return true;
    }

    @Override
    public boolean stopCellEditing() {
        if(getComponent() == null) {
            return true;
        }
        final JTextField tf = (JTextField)((MyPanel) getComponent()).getComponent(0);
        String value = tf.getText();
        if
         (!value.matches("[a-zA-Z0-9_]*")) {
            JOptionPane.showConfirmDialog(myEditor, "No special characters allowed", "Error",
                    JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    tf.requestFocusInWindow();
                }
            });
            
            return false;
        }
        fireEditingStopped();
        return true;
    }

    @Override
    public void cancelCellEditing() {
        fireEditingCanceled();
    }

    @Override
    public void addCellEditorListener(final CellEditorListener l) {
        listenerList.add(CellEditorListener.class, l);
    }

    @Override
    public void removeCellEditorListener(final CellEditorListener l) {
        listenerList.remove(CellEditorListener.class, l);
    }

    protected void fireEditingStopped() {//used in stopCellEditing
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingStopped(changeEvent);
            }
        }
    }

    protected void fireEditingCanceled() {//used in cancelCellEditing
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent);
            }
        }
    }

// implement TableCellEditor:
    @Override
    public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
        editorComponent = (JComponent) value;
        return editorComponent;
    }
}

class MyPanel extends JPanel {

    public MyPanel(final String value) {
        setLayout(new BorderLayout());
        final JTextField tf = new JTextField(value);
        final JButton text = new JButton("...");
        text.setPreferredSize(new Dimension(20, 20));
        text.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                String desc = JOptionPane.showInputDialog(text, "Type value");
                tf.setText(desc);
                tf.requestFocusInWindow();
            }
        });
        add(tf, BorderLayout.CENTER);
        add(text, BorderLayout.EAST);
    }
}
```


----------

