# Swing-MVC: JFrame verstecken und anderes anzeigen



## OlliL (20. Nov 2012)

Hallo,

ich habe eine Designfrage.

Ich bin dabei eine Applikation in mit Swing zu entwickeln und habe bisher Erfahrung mit SpringMVC, und möchte daher auch in Swing im MVC-Style entwickeln.

Nun zur Applikation:
Bevor die eigentliche Applikation startet, soll der User in einem extra JFrame sich mittels Benutzername/Passwort authentifizieren.
Nachdem diese Anmeldung geglückt ist, soll dieses JFrame verschwinden und die eigentliche Anmeldung - ein weiteres JFrame - soll sich öffnen.

Mein Design sieht bisher so aus:

1. ich habe ein LogonModel welches Observable erweitert.
- das LogonModel hat 2 private Variablen username+passwort und kümmert sich um das abgleichen dieser mit dem Backend über ein DAO via der Funktion doLogin().
- Stimmen username und passwort nicht überein, wird eine Fehlermeldung definiert und diese mittels notifyObservers(error) an die View zur Darstellung gegeben (z.B. Username leer, Passwort leer, Username oder Passwort falsch). Im Erfolgsfall wird eine leere Fehlermeldung zurück gegeben.

2. ich habe eine LogonView welche Observer implementiert.
- mit der update() Funktion wird die Fehlermeldung in einem sonst leeren JLabel dargestellt.

3. ich habe einen Controller welcher ActionListener implementiert.
- mittels actionPerformed wird username und passwort des Models gesetzt, und dann doLogin() des Models aufgerufen.


Beim Startup werden nun LogonModel und LogonView instanziert. LogonView wird als Observer beim LogonModel hinzugefügt.
Dem Controller wird LogonModel und LogonView hinzugefügt, und dem Button wird der Controller zugewiesen.

Das funktioniert alles soweit wunderbar. Nur frage ich mich nun.... wo bzw. wie soll ich nun die LogonView verstecken, und eine neue MainView öffnen?

Eine Möglichkeit welche mir gerade kommt ist:
1.) Ich könnte die MainView auch bereits komplett fertig bauen, jedoch mit setVisible(false).
2.) Dann könnte ich MainView auch als einen weiteren Observer dem LogonModel hinzufügen.
3.) Sollte dann das LogonModel mit einer leeren Fehlermeldung ankommen (=Logon erfolgreich) verstecke ich im update() des LogonScreen selbigen, und im MainScreen.update() wird dann setVisible(true) ausgefuehrt.
Irgendwie finde ich das aber nicht wirklich "sauber" und frage mich, ob es nicht eine bessere Möglichkeit gibt das beschriebene Problem zu loesen...

Gruesse


----------



## Camino (20. Nov 2012)

Nimm doch für die Anmeldung (Login) einen JDialog und für deine Hauptanwendung einen JFrame. Dann kommt bei Programmstart zuerst der JDialog mit dem Login und der Überprüfung und je nach Eingabe wertes du das aus und machst weiter mit dem JFrame (oder auch nicht).


----------



## KrokoDiehl (20. Nov 2012)

Nun im MVC kümmiert sich der Controller um die Steuerung. Das heißt dein LogonController sollte auch das schließen des LogonViews und das Öffnen des MainViews (bzw. über einen MainController) erledigen.

Dass ein MainView auch noch auf das LogonModel achtet ist m.E. auch unsauber. MVC kann in der Praxis immer etwas anders aussehen, daher schreibe ich mal, wie ich es in etwa machen würde, hier in Pseudocode:


```
class LogonView {
...
        JButton btnLogin = ...;
        btnLogin.addActionListener(new ActionListener() {
            public void actionPerformed(...) {
                logonCtrl.tryLogin();
            }
        };
...
}

class LogonController {
...
    public void tryLogin() {
        LogonView view = getView();

        // wie auch immer du die Logindaten prüfst...
        try {
            getModel().checkLogin(view.getUser(), view.getPassword());
        }
        catch (LoginException exc) {
            view.showError(...);    
            return;
        }

        view.close(); 
        //ggfs weiteres aufräumen
        new MainController().start(); 
    }
```

Ich hoffe es ist damit klar, wie der Zusammenhang zwischen den Dreien ist. Tatsächlich hat der View hier wenig mit dem Modell direkt zutun. Aber Logins sind dafür auch eher untypische Beispiel


----------



## bERt0r (20. Nov 2012)

Wenn du das alles auf rein MVC machen willst, sollte dein Model beim erfolgreichen Login ein Event werfen. Das Event kann dann von einem übergeordneten Controller abgefangen werden der dann die View ändert (von Login zu Mainview).


----------



## OlliL (20. Nov 2012)

So in etwa hatte ich es "vorher" auch. Ich meine: die View selber war ActionListener welcher nur den Controller aufrief und danach den Fehler angezeigt hat. Der Controller selber hat dann das Login gemacht, dann ggf. die Logon-Ansicht versteckt und die Main-Ansicht angezeigt oder eben einen Fehler zurueckgemeldet.

Dann.... bin ich aber auf The simplest Model View Controller (MVC) Java example gestoßen und fand das ganze als Observer Pattern zu realisieren wesentlich besser.

Nun stehe ich aber halt vor dem Problem, das mein Controller eigentlich nur das actionPerformed macht:


```
public class LogonController implements ActionListener{
	private LogonModel model;
	private LogonScreen view;

	public void actionPerformed(ActionEvent e) {
		model.setUsername(view.getUsername());
		model.setPassword(view.getPassword());
		model.doLogin();
	}

	public void addModel (LogonModel m) {
		this.model = m;
	}
	
	public void addView(LogonScreen v) {
		this.view = v;
	}
}
```

Nun könnte ich natürlich im actionPerformed auch die view verstecken (view.setVisible(false)) und dann das ganze MainView Gedöns "loslegen" aber ist das sauber?



```
public void actionPerformed(ActionEvent e) {
		model.setUsername(view.getUsername());
		model.setPassword(view.getPassword());
		if (model.doLogin()) {
			view.setVisible(false)
			MainModel mmodel = new MainModel();
			MainView mview = new MainView();

			mmodel.addObserver(mview);

			MainController controller = new MainController();
			controller.addModel(mmodel);
			controller.addView(mview);

			mview.addController(controller);
		}
	}
```

Irgendwie empfinde ich es als unsauber dies im actionPerformed in meinem LogonController zu "verstecken".
Irgendwie habe ich das Gefühl einen "Übercontroller" zu brauchen der sich nur darum kümmert:
- Programmstart -> LogonView anzeigen
- LogonView sagt "User hat sich angemeldet"
- LogonView verstecken
- MainView anzeigen
Das empfinde ich irgendwie als sauberer - oder empfinde ich falsch? 

*Edit*: bERt0r scheint genau das zu empfehlen - einen "Übercontroller" - mein Empfinden scheint nicht so falsch zu sein  
Events.... muss ich mich mal mit beschäftigen.


----------



## OlliL (20. Nov 2012)

Gibts was gutes zu lesen wie man Events selber zünden kann und darauf reagieren kann?


----------



## bERt0r (20. Nov 2012)

google + java create your own events = How To Create Your Own Events In Java  Chad.Stever


----------



## OlliL (20. Nov 2012)

Jo OK, das hatte ich auch schon gefunden... hatte mich gewundert, das es nicht irgendwie mit weniger Code geht 
Mir fehlt noch das "große ganze" aus dem Beispiel - n Bild wäre ganz gut gewesen.

Event Handling - Wikibooks, open books for an open world

ist auch zu finden. Ich wurschtel mich morgen mal da durch


----------



## KrokoDiehl (21. Nov 2012)

Dein genanntes Beispiel mit dem "simplest mvc" macht aber doch auch das, was ich vorgeschlagen habe:

Aktionen die im View ausgelöst werden, werden im Controller behandelt. Meistens löst das was im Modell aus. Das Modell propagiert Änderungen über Listener/Oberserver-Muster worauf der View und ggfs. auch der Controller reagieren kann.

Wie gesagt ist da Login-Beispiel ungünstig um MVC zu erklären, da es keine Daten zum anzeigen gibt, vielmehr geht es um einen Zustand "eingeloggt/ausgeloggt".
Ich halte es daher nicht für unsauber, dass der Controller die Fehlerbehandlung (Logindaten falsch) übernimmt.
Man kann es natürlich auch weiter aufteilen:
1. View: Beim Klick auf "Login" leitet der Ctrl es direkt ans Modell weiter
2. Modell: Das Modell prüft Logindaten und löst danach Event/Oberserver aus: Ok oder Fehlgeschlagen
3. View: Reagiert je nach Event anders: Beim Ok geht es wieder an den Ctrl und sagt: LoginOk, Schließ mich und starte Hauptanwendung. Bei Fehlgeschlagen gibts halt ne Meldung.

Aber der Vorschlag mit dem "Über"controller ist natürlich auch ein gangbarer Weg, den ich nicht für unsauber halte. Höchstens für etwas zu gekünstelt 
Aber Meinungen können diesbezüglich auseinander gehen. Ich hoffe ich konnte zumindest verdeutlichen, dass es durchaus mehrere Lösungen / Wege gibt


----------



## OlliL (21. Nov 2012)

Jo, du hast schon recht, aber ich glaube, diese Event-Geschichte kann ich spaeter auch noch gebrauchen.

Meine "Hauptanwendung" besteht aus mehreren JPanels welche über ein Menu eingeblendet werden (Quasi "Option 1" -> Zeige Jpanel 1, "Option 2" -> Verstecke Jpanel 1, zeige Jpanel 2)

Das koennte man dann ja auch über den Eventhandler machen. Man haette dann quasi eine Stelle die sich nur um die "Menusteuerung" der Applikation kümmert, Frames und/oder Panels anzeigt.

Andernfalls würde man diese Steuerung in dem Controller des Logon-Frames unterbringen und in dem Controller des Menu-Frames... geht alles, klar, aber "eine Stelle" find ich irgendwie mehr sexy


----------



## bERt0r (21. Nov 2012)

Was ich noch sagen wollte, wenn du willst brauchst du auch nicht eigene Events zu verwenden sondern kannst z.B auf ChangedEvents oder PropertyChangedEvents zurückgreifen.


----------

