# JTable - verlassen einer Zelle



## MScalli (26. Jan 2009)

Hi Leutz.
Ich habe eine JTable inkl DefaultTableModel und will erreichen das sobald die zelle verlassen wird überprüft wird ob sich der Inhalt verändert hat, wenn ja soll dies auch in der Datenbank verändert werden.

Habe eigentlich alles ohne Probleme hin bekommen.
Ich habe im model die Methode setValueAt(Object val, int row, int col) überschrieben und alles hat wunderbar geklappt.

Jetzt mein Problem. 
Wenn ich die Tabelle nicht durch tab, return oder klicken in ne andere Zelle verlasse sondern einfach in ein JTextField klicke wird die Methode nicht aufgerufen.


----------



## SlaterB (26. Jan 2009)

table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);


----------



## MScalli (26. Jan 2009)

funktioniert leider nur teilweise.
jetzt wird die Methode setValueAt am Anfang 2 mal aufgerufen.
Ausserdem komm ich bei den ersten paar mal nicht mal in die Zelle rein.

Wenn ich auf die erste Zelle klicke(die ist nicht editierbar) und dann auf eine andere gehts einwandfrei??!?!?


```
// TableModel Sortierung geht von alleine!!
      model = new DefaultTableModel(data, columnNames) {
        private static final long serialVersionUID = -7946186467395546994L;
        public Class<?> getColumnClass(int column) {
          return getValueAt(0, column).getClass();
           
        }
        public void setValueAt(Object val, int row, int col) {
        	System.out.println("In setValueAt() --> verlassen einer Zelle der Tabelle");
        	
  	     // Abfrage JA/NEIN/ABBRECHEN
            int antwort = JOptionPane.showConfirmDialog(frame, "Datensatz wirklich ändern?", "Ändern Datensatz!!", 1);
            
            if(antwort ==  JOptionPane.YES_OPTION)
            {
                      // hier wird alles mögliche gemacht
            }  
        }
```


----------



## SlaterB (26. Jan 2009)

tja, das Leben ist schon hart, 
vielleicht den Property erst setzen, wenn du schon drin bist oder so


----------



## MScalli (26. Jan 2009)

Habe es jetzt in die SetValueAt Methode gepackt.
Am anfang gehts jetzt einwandfrei, doch wenn ich in ein TextFeld klicke und dann wieder in die JTable habe ich wieder das selbe Problem


----------



## MScalli (26. Jan 2009)

gibt es auch ne andere möglichkeit?

kann ich evtl. abfragen ob der Cursor gerade in einer Zelle ist(wenn ja wie?) und nur dann die setValueAt() Methode aufrufen?


----------



## Ebenius (26. Jan 2009)

MScalli hat gesagt.:
			
		

> funktioniert leider nur teilweise.
> jetzt wird die Methode setValueAt am Anfang 2 mal aufgerufen.[/code]


Das klingt seltsam: Mach mal ein kleines selbstständiges Beispiel, das das Problem enthält. Und schreib dazu, was ich genau klicken muss, damit das passiert. Entweder kann es dann jemand nachvollziehen, oder Du findest auf dem Weg genau das Problem.

Ebenius


----------



## MScalli (27. Jan 2009)

Beim erstellen dieses Lauffähigen Programms hab ich den Fehler gefunden, weiss aber leider nicht wie ich dieses Problem beheben kann.
Ich Probier es einfach mal so zu posten denn um das alles zum laufen zu bringen brauch ich noch 2 Stunden 
Und ich häng hier schon ewig!!


Das Problem ist der CellEditor den ich benutze um die Hintergrundfarben in der JTable zu setzten wenn ich die Zellen betrete.

Ich rufe die Methode initColumn() auf um die Spaltenbreiten,  Dokumententypen usw. festzulegen


```
private void initColumn(final JTable table, int[] _width_array, Object[][] _arr_decimal_places) {
        TableColumn column = null;
        
        for (int i = 0; i < _width_array.length; i++) {
        	
            final JFormattedTextField tf = new JFormattedTextField();
            
            //ACHTUNG: Weitere Document-Typen in Klasse "MyTableCellEditor" anpassen. 
            if(_arr_decimal_places[i][0].toString().equals("C")){
            	tf.setDocument(new CharDocument((Integer) _arr_decimal_places[i][1]));
            }
            if(_arr_decimal_places[i][0].toString().equals("I")){
            	TabDecimalDocument ldd = new TabDecimalDocument((Integer) _arr_decimal_places[i][1] + 1);
            	ldd.setPlacesBeforDecimalPoint((Integer) _arr_decimal_places[i][1] - (Integer) _arr_decimal_places[i][2]);
            	ldd.setPlacesAfterDecimalPoint((Integer) _arr_decimal_places[i][2]);
            	tf.setDocument(ldd);
            }   
            if(_arr_decimal_places[i][0].toString().equals("D")){
            	tf.setDocument(new DateDocument());
            }  
    		
            column = table.getColumnModel().getColumn(i);
            //Umgeschriebener CellEditor, der Hintergrundfarben festlegt --> der ist das Problem
            column.setCellEditor(new MyTableCellEditor(tf,_arr_decimal_places[i][0].toString()));
            column.setPreferredWidth(_width_array[i]);  
        }
	}
```

wenn ich diese Zeile entferne würde es gehen

```
column.setCellEditor(new MyTableCellEditor(tf,_arr_decimal_places[i][0].toString()));
```

und hier mein CellEditor

```
package FerixClient;

 import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JFormattedTextField;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;

/**
 * Klasse MyTableCellEditor
 * Erstellt Textfeld zum editieren von Zellen in Tabellen 
 *
 */

  public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor{

	private static final long serialVersionUID = 1L;
	protected EventListenerList listenerList = new EventListenerList();
    protected ChangeEvent changeEvent = new ChangeEvent(this);
    JFormattedTextField txf;
    String fieldtype;

    public MyTableCellEditor(JFormattedTextField tf, String type) {
      txf = tf;
      fieldtype = type;
    	//ActionListener für Return-Taste
    	txf.addActionListener(new ActionListener() {
    		
    	  
        public void actionPerformed(ActionEvent event) {
          fireEditingStopped();
        } 
      });
    }

	public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int rowIndex, int vColIndex) {

		if (isSelected) {
	          txf.setBackground(Color.YELLOW);
	          txf.setSelectionStart(0);
		      txf.setSelectionEnd(txf.getText().length());
	          
	        } else {
	          txf.setBackground(Color.YELLOW);
	          txf.setSelectionStart(0);
		      txf.setSelectionEnd(txf.getText().length());
	        } 

			txf.setText(String.valueOf(value));
			
			return txf;
	}
	
	public Object getCellEditorValue() {

		if(txf.getText().equals("")){
			if(fieldtype.toUpperCase().equals("C")){
				return "";
			}else if(fieldtype.toUpperCase().equals("I")){
				return 0;
			}
		}else{
			return txf.getText();
		}
		return 0;
	}
	
}
```

Wegen diesem CellEditor wird die Methode setValueAt() zu früh und zu oft aufgerufen.
Hat wer ne Idee wie ich das lösen könnte?


----------



## Ebenius (27. Jan 2009)

Ich sehe das Problem des CellEditors nicht. Wenn Du in getCellEditorValue() Debug-Output einbaust, wird der dann doppelt ausgegeben?


----------



## MScalli (27. Jan 2009)

ja. obwohl ich nicht mal in der Zelle bin!!
woher weisst jetzt das 

Genau das ist das Problem.
deswegen wird die setValueAt() Methode auch aufgerufen.
eigentlich sollte sie immer nur beim verlassen aufgerufen werden, besser gesagt das was ich in der Methode mache soll nur dann ausgeführt werden.


----------



## Ebenius (27. Jan 2009)

Okay... gib mal in getCellEditorValue() das da aus: 
	
	
	
	





```
new Exception("Test").printStackTrace();
```
Mal schauen wo das her kommt.

Ebenius


----------



## MScalli (27. Jan 2009)

hmm ok. versteh ich jetzt kein wort aber ich habs mal eingebaut.
Die ausgabe ist folgende!

java.lang.Exception: Test
	at FerixClient.MyTableCellEditor.getCellEditorValue(MyTableCellEditor.java:63)
	at javax.swing.JTable.editingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.fireEditingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.stopCellEditing(Unknown Source)
	at javax.swing.JTable$CellEditorRemover.propertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
In setValueAt() --> verlassen einer Zelle der Tabelle
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
*************** getCellEditorValue
java.lang.Exception: Test
	at FerixClient.MyTableCellEditor.getCellEditorValue(MyTableCellEditor.java:63)
	at javax.swing.JTable.editingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.fireEditingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.stopCellEditing(Unknown Source)
	at javax.swing.JTable$CellEditorRemover.propertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.KeyboardFocusManager.dispatchAndCatchException(Unknown Source)
	at java.awt.KeyboardFocusManager.processCurrentLightweightRequests(Unknown Source)
	at java.awt.KeyboardFocusManager$1.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.Dialog$1.run(Unknown Source)
	at java.awt.Dialog$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.awt.Dialog.show(Unknown Source)
	at javax.swing.JOptionPane.showOptionDialog(Unknown Source)
	at javax.swing.JOptionPane.showConfirmDialog(Unknown Source)
	at javax.swing.JOptionPane.showConfirmDialog(Unknown Source)
	at javax.swing.JOptionPane.showConfirmDialog(Unknown Source)
	at FerixClient.CreateTableFromDBWithSQL$4.setValueAt(CreateTableFromDBWithSQL.java:292)
	at javax.swing.JTable.setValueAt(Unknown Source)
	at javax.swing.JTable.editingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.fireEditingStopped(Unknown Source)
	at javax.swing.AbstractCellEditor.stopCellEditing(Unknown Source)
	at javax.swing.JTable$CellEditorRemover.propertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.firePropertyChange(Unknown Source)
	at java.awt.KeyboardFocusManager.setGlobalPermanentFocusOwner(Unknown Source)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)In setValueAt() --> verlassen einer Zelle der Tabelle

	at java.awt.EventDispatchThread.run(Unknown Source)


----------



## Ebenius (27. Jan 2009)

Das Problem scheint Deine setValueAt()-Methode im TableModel zu sein. Diese baut ein JOptionPane auf. Das macht man grundsätzlich nicht in Modellen. Es führt in diesem Fall dazu, dass der noch nicht vollständig bearbeitete Focus-Lost-Event auf der Tabelle nochmal bearbeitet wird, weil innerhalb derer der Focus auf das JOptionPane geschoben wird. Daher wird zweimal setValueAt() aufgerufen. Verständlich?

Dort musst Du das Design überarbeiten!


----------



## MScalli (27. Jan 2009)

Ja ich glaub ich hab das kapiert. Die OptionPane geht auf, bekommt den focus und beim schliessen geht der Focus wieder zurück auf die Zelle.

Das Problem ist das ich beim verlassen der Zelle eine Abfrage mache ob der User die änderung in der Datenbank speichern will. Ich weiss ehrlich gesagt nicht genau wie/ oder wo ich das anders lösen sollte.
Wenn ich das mit der hintergrundfarbe beim betreten weg lasse geht ja alles, aber was mache ich wenn ich so nen CellEditor mal dringend brauche??
Häng da schon ziemlich lange und bin ehrlich gesagt mit meinem Latein am ende  
:bahnhof:


----------



## Ebenius (27. Jan 2009)

Schmeiß den JOptionPane-Kram aus dem Modell raus; hat ohnehin nix da zu suchen. Mach's statt dessen wie in diesem Beispiel. So gehört's: 
	
	
	
	





```
public class CellEditorFun {

  private static class VerifyingCellEditor extends AbstractCellEditor
    implements TableCellEditor {

    final JTextField tf = new JTextField();
    private Object value;

    /** Creates a new <code>VerifyingCellEditor</code>. */
    VerifyingCellEditor() {
      tf.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
          stopCellEditing();
        }
      });
    }

    @Override
    public Component getTableCellEditorComponent(
          JTable table,
          Object value,
          boolean isSelected,
          int row,
          int column) {
      this.value = value;
      tf.setText(value == null ? null : value.toString());
      return tf;
    }

    @Override
    public Object getCellEditorValue() {
      System.out.println("Getting my text");
      return value;
    }

    @Override
    public boolean stopCellEditing() {
      final int result =
            JOptionPane.showConfirmDialog(SwingUtilities
                  .getWindowAncestor(tf), "Really change record?", "Sure?",
                  JOptionPane.YES_NO_OPTION);
      if (JOptionPane.YES_OPTION == result) {
        this.value = tf.getText();
        fireEditingStopped();
        return true;
      } else {
        return false;
      }
    }
  }

  /**
   * Test main method.
   * 
   * @param args ignored
   */
  public static void main(String[] args) {
    final JPanel contentPane = new JPanel(new BorderLayout(6, 6));
    final JTable table = new JTable(5, 5);
    table.setDefaultEditor(Object.class, new VerifyingCellEditor());
    table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    contentPane.add(new JScrollPane(table));
    contentPane.add(new JButton("Move focus to here!"), BorderLayout.EAST);

    final JFrame f = new JFrame("Cell Editor Fun");
    f.setContentPane(contentPane);
    f.pack();
    f.setLocationRelativeTo(null);
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.setVisible(true);
  }
}
```

HTH, Ebenius


----------



## MScalli (28. Jan 2009)

Wow, vielen Dank.

werd mal meinen Code umschreiben.. denn da muss jetzt einiges geändert werden ^^
ausserdem muss ich mir das mal genau anschaun.. das kann bisschen dauern^^


----------

