# JFrame + JTable + main-Methode + Programmsteuerung



## andy77 (14. Okt 2007)

Hallo,

ich habe ein JTable in einem JFrame eingebettet und ein Objekt "EingabeMaske" dieser Klasse wird in einer main-Methode instanziiert und eingeblendet.



```
EingabeMaske eingabe = new EingabeMaske();
eingabe.setVisible(true);
```


Mein JFrame dient als Eingabemaske, daher sollen die nachstehenden Anweinsungen der main-Methode erst ausgeführt werden, wenn die Eingabe abgeschlossen wurde. Erst nach der Betätigung eines OK-Buttons sollen die nachstehenden Anweisungen der main-Methode ausgeführt werden. Mein Fenster soll auch noch nach der Eingabe geöffnet bleiben.


Mein Problem:      Die Abfolge der main-Anweisungen werden bislang nicht unterbrochen!


Gibt es eine Methode der Klasse JFrame, die erst nach der Abarbeitung des JFrame die nachfolgenden Anweisungen der main-Methode zulässt?



Vielen Dank für eure Hilfe!


----------



## André Uhres (14. Okt 2007)

Setz einfach die nachstehenden Anweisungen der main-Methode in die "actionPerformed", die bei der Betätigung des OK-Buttons aufgerufen wird.


----------



## andy77 (14. Okt 2007)

Danke André für Deine Antwort.

Zu Testzwecken habe ich Deinem Vorschlag bereits umgesetzt, aber in der abschließenden Version möchte ich so nicht implementieren.

Ich möchte eine klare Umsetzung der MVC-Architektur, daher möchte ich die Programmsteuerung nicht in der JFrame Klasse unterbringen. 


Ich möchte in meiner main-Methode die Programmsteuerung ausführen, dadurch ist die Implementierung deutlich übersichtlicher.


Bislang habe ich noch keine Erfahrungen mit der Klasse JDialog. Ich hatte eine Idee, dass ich nicht die JFrame-Klasse verwende, sondern JDialog. Ich nehme an, dass der Programm-Ablauf der main-Methode erst fortführen würde, wenn das Dialog-Fenster geschlossen wäre.


Über weitere Tipps wäre ich sehr dankbar!


----------



## André Uhres (15. Okt 2007)

Die main sollte imho die Anwendung starten, nicht steuern.
Sie könnte z.B. Model, View und Controller anlegen und die View sichtbar machen. 
Damit wäre der Startup schon erledigt und die main beendet. 
Danach sollte die MVC Architektur in der Lage sein, selbständig zu funktionieren.


----------



## HLX (15. Okt 2007)

andy77 hat gesagt.:
			
		

> Ich möchte eine klare Umsetzung der MVC-Architektur, daher möchte ich die Programmsteuerung nicht in der JFrame Klasse unterbringen.



Wenn dir diese Trennung wichtig ist, implementierst du einfach einen eigenen Listener.


```
public class AbsendenListener implements ActionListener {

    private AbstractButton button; 

    public AbsendenListener(AbstractButton aButton) {
           button = aButton;
           button.addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        ...
    }
}
```


----------



## andy77 (15. Okt 2007)

Danke HLX,


bisher habe ich noch keinen eigenen Listener geschrieben, ich werde mich erstmals in dieses Thema einarbeiten müssen. Wenn ich die Zeit zum testen finde, dann werde ich das sogleich ausprobieren.

Danke nochmals .. bye


----------



## André Uhres (15. Okt 2007)

Ich hab nix gegen einen eigenen Listener. 
Aber ich versteh nicht, was das mit der MVC-Architektur zu tun hat. 
Würde mir das bitte jemand erklären?


----------



## HLX (15. Okt 2007)

André Uhres hat gesagt.:
			
		

> Ich hab nix gegen einen eigenen Listener.
> Aber ich versteh nicht, was das mit der MVC-Architektur zu tun hat.
> Würde mir das bitte jemand erklären?



Durch die implementierung eigener Listener können Controller-Komponenten auf simple Weise aus der GUI gezogen werden. Der Frame kennt so seine Controller-Komponenten nicht mehr - lediglich der Listener weiß alles: er kennt seine GUI-Komponenten und kommuniziert mit dem Back-End. 

Es ist eigentlich auch sinnvoller anstelle des Buttons den Frame im Konstruktor des Listeners zu übergeben, damit der Listener ggf. die GUI nach einer Benutzerinteraktion anpassen kann (z.B. sperren oder freigeben von Buttons etc.).


----------



## André Uhres (15. Okt 2007)

Was verstehst du unter "Controller-Komponenten" und wie passen sie in das MVC Konzept?
Wie ich es verstanden habe, soll der Controller das Model verwalten und alle registrierten Views über eventuelle Änderungen informieren.


----------



## HLX (15. Okt 2007)

Um die Begriffe nochmal klar abzugrenzen hier eine kurze Erläuterung:

Model = Anwendungslogik + DB (BackEnd)
View = reine Darstellungsschicht ohne Logik oder Steuerung

Der Controller stellt eine Schnittstelle zwischen Model und View dar. Er empfängt Events von der GUI und kommuniziert mit dem Backend. Er entscheidet über die Gestaltung der GUI, z.B. abhängig vom Ergebnis der der Verarbeitung im Model. Der Controller nimmt damit lediglich Steuerungsaufgaben war.

Diese Rolle kann von Listenern übernommen werden. Ich habe sie als Controller-Komponenten bezeichnet, da sie jeweils nur für einen bestimmten Kontext gelten und nicht der gesamten Anwendungskontrolle dienen.


----------



## andy77 (15. Okt 2007)

@HLX

Du scheinst schon sehr erfahren mit der GUI-Programmierung zu sein, ich steige erst in diesen Stoff ein. Kennst Du im Internet gute Codebeispiele?

Mir ist z.B. unklar, an welcher Stelle ich Deine Klasse AbsendenListener einbinden muss? In der main-Methode? Und wie muss diese Klasse eingebunden werden?


Zum besseren Verständnis möchte ich eine ganz primitive Anwendung darlegen. Es wäre super klasse, HLX, wenn du einen Lösungsweg aufzeigen würdest.

Über den VisualEditor von eclipse erstelle ich ein JFrame, dieser Frame enthält einen JTable und einen JButton. Die Tabelle habe ich bereits implementiert. Diese Tabelle dient als Eingabemaske. Wenn die Eingabe abgeschlossen ist, sollte der Button betätigt werden. Daraufhin wird die Tabelle ausgelesen und die Werte werden einem Objekt einer selbstgeschriebenen Klasse übergeben. All das habe ich bereits implementiert und es funktioniert.

Nun zu meinem Problem, wie sieht die main-Methode aus und binde ich Listener ein? 


Meine Bezeichnungen:
     class EingabeMaske       --> JFrame
     class MyTableModel       --> definiert das Modell für meine Tabelle
     class DatenVerwaltung   --> hier wurden Attribute definiert, die die Werte speichern. Auch Methoden zum auslesen                    
                                              der Tabelle und zur weiteren berechnung

    class mainKlasse            --> diese enthält die ausführende Main-Methode 



Solltest Du die Zeit und Lust haben um meine Frage zu beantworten, so möchte ich Dir vorab schon ganz herzlich danken!


----------



## André Uhres (16. Okt 2007)

andy77 hat gesagt.:
			
		

> ..Solltest Du die Zeit und Lust haben um meine Frage zu beantworten,
> so möchte ich Dir vorab schon ganz herzlich danken!


Dem kann ich mich nur anschliessen  
Ich hab zwar GUI Erfahrung und prinzipiell halte ich auch die Daten 
immer unabhängig von GUI und Logik.
Aber von MVC hab ich bis jetzt nur Theorie gesehen 
und ganz simple Beispiele mit dem Observer/Observable Pattern.
Ein etwas realistischeres Beispiel würde mich freuen.
Sowas könnte man dann auch mal in die FAQ einbauen.
Vielleicht ist das was ich mache schon MVC, ohne dass es mir bewusst ist :wink:
Allerdings, dass die main-Methode ein Programm regelrecht steuern soll, 
das ist mir bis jetzt noch nicht vorgekommen.


----------



## andy77 (16. Okt 2007)

Hallo André,

danke, dass Du ebenfalls meinen Wunsch ausgesprochen hast, denn ich dachte schon, dass ich unverschämt wäre.  

Bislang sind mir Observer Pattern unbekannt, danke für Deinen Link, den werde ich mir mal genauer anschauen.


----------



## HLX (16. Okt 2007)

Erfahrung in GUI-Entwicklung ist vielleicht etwas übertrieben. MVC in der Praxis ist mir vor Allem aus Web-Projekten mit Apache Struts geläufig. Mit dem Observer-Pattern habe ich bis jetzt noch nicht gearbeitet. Vielleicht ist dies auch besser für MVC geeignet. Ich habe mal ein kleines Swing-Beispiel mit nem Listener geschrieben. Seht´s euch mal an - dann können wir darüber diskutieren.

*Anwendungs-Controller*

```
public class ApplicationController {

	public ApplicationController() {
		initialize();
	}
	
	// Initialisierung der Anwendung, Erzeugung des Frames, Zuweisung der Listener
	private void initialize() {
		EingabeMaske maske = new EingabeMaske();
		BestaetigenListener listener = new BestaetigenListener(maske);
		WindowListener l = new WindowAdapter() {
			 public void windowClosing(WindowEvent e) {
				 System.exit(0);
			 }
		};
		maske.addWindowListener(l);
	}

	public static void main(String[] args) {
		ApplicationController controller = new ApplicationController();
	}

}
```

*Frame*

```
// reine grafische Oberfläche ohne Steuerung und ohne Kontakt zur Business-Logik
public class EingabeMaske extends JFrame {
	
	private JPanel contentPane = null;
	private JButton okButton = null;
	private JTextField statusField;
	
	public EingabeMaske() {
		super();
		setSize(250, 100);
		setContentPane(getFrameContentPane());
		setVisible(true);
	}
	
	javax.swing.JPanel getFrameContentPane() {
		if (contentPane == null) {
			contentPane = new JPanel();
			contentPane.add(getOkButton());
			contentPane.add(getStatusField());
			contentPane.setVisible(true);
		}
		return contentPane;
	}
	
	JButton getOkButton() {
		if (okButton == null) {
			okButton = new JButton("OK");
		}
		return okButton;
	}
	
	JTextField getStatusField() {
		if (statusField == null) {
			
			statusField = new JTextField("Neu");
			statusField.setPreferredSize(new Dimension(100,20));
			statusField.setEnabled(false);
			statusField.setVisible(true);
		}
		return statusField;
	}
}
```

*Listener*

```
// Schnittstelle zwischen GUI und Business-Logik
public class BestaetigenListener implements ActionListener {

	private EingabeMaske maske;
	
	public BestaetigenListener(EingabeMaske aMaske) {
		super();
		maske = aMaske;
		maske.getOkButton().addActionListener(this);
	}

	private EingabeMaske getMaske() {
		return maske;
	}
	
	public void actionPerformed(ActionEvent newE) {
		try {
			MyModel model = new MyModel();
			// Aufruf der Business-Lokik
			String text = model.createText(getMaske().getStatusField().getText());
			// Verändern der GUI aus dem Ergebnis der BL
			getMaske().getStatusField().setText(text);
		}
		catch (Exception e) {
			// GUI-Steuerung im Fehlerfall
			String error = "Schwerer Fehler";
              JOptionPane.showMessageDialog(getMaske(), error);
			getMaske().getStatusField().setText(error);
		}
	}
}
```

*Model*

```
// für verschiedene GUIs wiederverwendbare Business-Logik
public class MyModel {

	public MyModel() {
		super();
	}
	
	public String createText(String text) throws Exception {
		if(text.equalsIgnoreCase("kein fehler")) {
			throw new Exception("Fehler");
		}
		return "Kein Fehler";
	}
}
```


----------



## andy77 (16. Okt 2007)

Hallo HLX!


VIELEN DANK !!!  Dank Deiner Arbeit werde ich mich bestmöglich in diese Thematik einarbeiten können. Nochmals VIELEN DANK!


----------



## andy77 (16. Okt 2007)

@ HLX

ich habe sofort Deinen Beispiel-Quellcode studiert und an meiner Anwendung angepasst. Meine Application läuft noch nicht in der richtigen Reihenfolge.

Mein Fehler vermute ich in der Klasse EingabeListener oder in der Klasse ControllClass.

Zuerst wird ein Frame mit einer Tabelle geöffnet, in diese Tabelle soll der Anwender Integer Werte hinschreiben, andere Eingaben werden nicht akzeptiert. Mit Betätigung des Button wird die Tabelle ausgelesen, die Werte aus der Tabelle werden einer Klassenvariablen der ControllClass übergeben. Anschließend wird das Frame geschlossen.

Bis zu diesem Punkt läuft die Anwendung korrekt, nun beschreibe ich die falsche Abfolge. In der main-Methode der ControllClass wird durch einen Methodenaufruf das Einlesen aus der Tabelle angestoßen.


```
public static void main(String[] args) 
{
	steuerungEingabe();
		
	daten.ausgabeKonsole();		
}
```

Die nächste Anweisung "daten.ausgabeKonsole();" sollte erst ausgeführt werden, wenn die Eingabe abgeschlossen wurde und die Werte der Tabelle in die Klassenvariable "daten" geschrieben wurde. 

Bislang öffnet sich der Frame und sofort wird das Array "daten" auf die Konsole ausgeschrieben. Weil die Eingabe noch nicht erfolgte und daher keine Wertzuweisung erfolgen konnte, ist das Array "daten" noch leer.


Wie kann ich mein Problem lösen ?????  


Meinen Quellcode füge ich bei:


ConntrollClass

```
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowListener;

public class ControllClass 
{
	//	Klassenvariable
	public static DatenVerwaltung daten = new DatenVerwaltung();
	
	
	public static void steuerungEingabe()
	{
		JFrameEingabeMaske maske = new JFrameEingabeMaske();
		maske.setVisible(true);
		EingabeListener el = new EingabeListener(maske);
		WindowListener wl = new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		};
		
		maske.addWindowListener(wl);
	}
	
	public static void main(String[] args) 
	{
		steuerungEingabe();
		
		daten.ausgabeKonsole();		
	}
}
```

EingabeListener

```
import java.awt.event.*;

public class EingabeListener implements ActionListener 
{
	//	Attribut
	private JFrameEingabeMaske eingabeMaske;
	
	
	
	//	Konstruktor
	public EingabeListener(JFrameEingabeMaske maske)
	{
		super();
		this.eingabeMaske = maske;
		this.eingabeMaske.getJButton().addActionListener(this);
	}
	
	
	//	Methoden
	private JFrameEingabeMaske getEingabeMaske()
	{
		return this.eingabeMaske;
	}
	
	
	public void actionPerformed(ActionEvent newE)
	{
		int[][] eingeleseneWerte = new int[9][9];
		
		for(int zeile = 0; zeile < 9;zeile++)
		{
			for(int spalte = 0; spalte < 9; spalte++)
				eingeleseneWerte [zeile][spalte] = Integer.parseInt(this.getEingabeMaske().getJTable().getModel().getValueAt(zeile, spalte).toString());
		}

// 	Die Werte aus der Tabelle werden der Klassenvariable aus der ControllClass Klasse übergeben
		ControllClass.daten.setDaten(eingeleseneWerte);
		
//	Zur Kontrolle werden an dieser Stelle das Array auf die Konsole ausgeschrieben
		System.out.println("Diese Ausgabe wurde aus der Klasse EingabeListener gestartet \nund schreibt die Eingabe aus der Tabelle heraus");
		ControllClass.daten.ausgabeKonsole();
		System.exit(0);
	}
}
```


JFrameEingabeMaske

```
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JTable;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JLabel;

public class JFrameEingabeMaske extends JFrame {

	private static final long serialVersionUID = 1L;

	private JPanel jContentPane = null;

	private JTable jTable = null;

	private JButton jButton = null;
	
	

	public JTable jTableErg = null;

	private JLabel jLabel = null;

	/**
	 * This is the default constructor
	 */
	public JFrameEingabeMaske() {
		super();
		initialize();
	}

	
	private void initialize() {
		this.setSize(538, 356);
		this.setContentPane(getJContentPane());
		this.setTitle("Sudoku");
	}

	
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jLabel = new JLabel();
			jLabel.setBounds(new Rectangle(18, 9, 190, 31));
			jLabel.setText("Bitte geben Sie Ihre Eingabe ein:");
			jContentPane = new JPanel();
			jContentPane.setLayout(null);
			jContentPane.add(getJTable(), null);
			jContentPane.add(getJButton(), null);
	//		jContentPane.add(getJTableErg(), null);
			jContentPane.add(jLabel, null);
		}
		return jContentPane;
	}

	
	public JTable getJTable() {
		if (jTable == null) 
		{
			MyTableModel model = new MyTableModel();
			jTable = new JTable(model);
			jTable.setBounds(new Rectangle(13, 49, 308, 144));
		}
		return jTable;
	}

	
	public JButton getJButton() {
		if (jButton == null) 
		{
			jButton = new JButton();
			jButton.setBounds(new Rectangle(426, 275, 75, 36));
			jButton.setText("OK");
			
		}
		return jButton;
	}


	

}
```

MyTableModel

```
import javax.swing.table.AbstractTableModel;

public class MyTableModel extends AbstractTableModel
{
//	Attribute
	private int[][] gesamtMatrix;
	

//	Konstruktor
	public MyTableModel()
	{
		gesamtMatrix = new int[][]{{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0},
									{0,0,0,0,0,0,0,0,0}};
	}
	
	
	
//	 Die Anzahl Columns
    public int getColumnCount() 
    {
       return 9;
    }
 
    
    // Die Anzahl Rows
    public int getRowCount() 
    {
       return 9;
    }
  
    
//  Der Wert der Zelle (rowIndex, columnIndex)
    public Object getValueAt(int rowIndex, int columnIndex) 
    {
       return new Integer(gesamtMatrix[rowIndex][columnIndex]);
    }

    
//  Jede Zelle ist editierbar   
    public boolean isCellEditable(int rowIndex, int columnIndex) 
	{
	      return true;
	}
    
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) 
	{
	     gesamtMatrix[rowIndex][columnIndex] = ((Integer)aValue).intValue();
	}
    
    
//  Eine Angabe, welchen Typ von Objekten in den Columns angezeigt werden soll   
    public Class getColumnClass(int columnIndex) 
    {
        return Integer.class;
        
       
    }
    
}
```


----------



## HLX (17. Okt 2007)

Niemand zwingt dich "daten.ausgabeKonsole()" in der Main-Methode aufzurufen.  :wink: 

Deine Ausgabe basiert auf einer Benutzer-Interaktion und sollte daher nur in Verbindung mit dieser, also einem Listener aufgerufen werden. 

Den Application-Controller würde ich in Erster Linie zur Initialisierung deiner Anwendung verwenden und nicht zur Ablaufsteuerung. Erzeuge hier erstmal nur die Anwendung, instanziiere die einzelnen Komponenten (GUI, Listener etc.) und verbinde sie miteinander. 

Der Application-Controller ist auch kein Daten-Container. Zieh also die Datenverwaltung raus. Die Listener sollen die Daten beim Model anfordern. Im Zweifelsfall einfach ein Singleton für die Datenverwaltung nehmen:


```
public class Datenverwaltung {

	private static Datenverwaltung instance = null; 
	
	private Datenverwaltung() {
		super();
	}
	
	public static Datenverwaltung getInstance() {
		if(instance == null) {
			instance = new Datenverwaltung();
		}
		return instance;
	}

   public String getDaten() {
       ...
   }
}
```

Noch ein Tipp: Verwende keine public static-Methoden im Controller, dass verführt zur illegalen Verwendung dieser. Den Application-Controller würde ich zunächst vom Zugriff von außen abschotten, sprich, wie in meinem Beispiel: instanzieren und alle Methoden private. Sollte es Gründe geben, Informationen vom ApplicationController anzufordern, dann ggf. diesen auch zum Singleton umwandeln.


----------



## andy77 (17. Okt 2007)

@ HLX,

Ich werde erstmal am Wochenende in die Materie einsteigen, ich danke Dir für all Deine Hilfe. Ich habe immer noch nicht das Grundverständnis gewonnen, daher muss ich erst daran arbeiten.

Nochmals vielen Dank für Deine Hilfe. Ich brauche nun erst mal etwas Zeit.


----------



## andy77 (22. Okt 2007)

@ HLX
@ André Uhres


Ich habe in den letzten Tagen viel programmiert und konnte mein Problem beheben. Es war ganz einfach. Weil ich durch eine Einträge so viele Ideen und Eindrücke gewonnen habe, konnte ich schönes Programm GMM für die Ausgleichsrechnung entwickeln.


Nochmals vielen Dank!


----------

