SWT: Beenden Dialog beim Schließen

ryoshi

Grünschnabel
Guten Tag,

nach langer Zeit des "normalen" Java programmierens, mit den native Librarys von Sun, habe ich mich entschlossen eine Anwendung mit SWT / JFace zu schreiben.

Nachdem ich mich etwas in SWT eingelesen und rumprobiert hatte, hab ich versucht das ganze mit JFace zu implementieren, da das ja angeblich einfacher sein soll.

Nun zu meinem Problem:
Ich möchte gerne den User mit einem Beenden-Dialog nerven. Also eine Art "Wollen Sie das Programm wirklich beenden?" Ja-Nein Abfrage.

Wenn ich ohne JFace arbeite kann ich das sehr einfach mit einem ShellListener machen:
final MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
messageBox.setMessage("Möchten Sie das Programm wirklich schließen?");
messageBox.setText("Frage");

shell.addShellListener(new ShellAdapter()
{
public void shellClosed(ShellEvent arg0)
{
if(messageBox.open() == SWT.NO)
arg0.doit = false;​
}​
});
Durch das ShellEvent mit seinem Attribut "doit" kann ich die Aktion abbrechen.

Arbeite ich jetzt mit einem JFace und überschreibe wie gefordert die Methode "createContents(Composite parent)" und versuche da folgendes zu machen:
protected Control createContents(Composite parent)
{
final Shell shell = getShell();

ImageDescriptor programmIcon = ImageDescriptor.createFromFile(Applikation.class, "icons/programm_icon.gif");

shell.setSize(700, 500);
shell.setText("Applikation");
shell.setImage(programmIcon.createImage());

shell.addShellListener(new ShellAdapter()
{
public void shellClosed(ShellEvent e)
{
boolean beenden_Dialog = MessageDialog.openQuestion(shell, "Frage", "Wirklich Benden?");
if(!beenden)
e.doit = false;​
}​
});

return parent;​
}
Da gibts folgende rote Tapete:
org.eclipse.swt.SWTException: Widget is disposed
at org.eclipse.swt.SWT.error(SWT.java:3374)
at org.eclipse.swt.SWT.error(SWT.java:3297)
at org.eclipse.swt.SWT.error(SWT.java:3268)
at org.eclipse.swt.widgets.Widget.error(Widget.java:435)
at org.eclipse.swt.widgets.Widget.getDisplay(Widget.java:545)
at org.eclipse.jface.dialogs.IconAndMessageDialog.getSWTImage(IconAndMessageDialog.java:266)
at org.eclipse.jface.dialogs.IconAndMessageDialog.getQuestionImage(IconAndMessageDialog.java:247)
at org.eclipse.jface.dialogs.MessageDialog.<init>(MessageDialog.java:149)
at org.eclipse.jface.dialogs.MessageDialog.openQuestion(MessageDialog.java:364)
at de.annabamp.adressbuch.gui.Applikation$1.shellClosed(Applikation.java:39)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:159)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:928)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:952)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:937)
at org.eclipse.swt.widgets.Decorations.closeWidget(Decorations.java:257)
at org.eclipse.swt.widgets.Decorations.WM_CLOSE(Decorations.java:1606)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:3248)
at org.eclipse.swt.widgets.Decorations.windowProc(Decorations.java:1539)
at org.eclipse.swt.widgets.Shell.windowProc(Shell.java:1634)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:4025)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:1927)
at org.eclipse.swt.widgets.Shell.callWindowProc(Shell.java:443)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:3334)
at org.eclipse.swt.widgets.Decorations.windowProc(Decorations.java:1539)
at org.eclipse.swt.widgets.Shell.windowProc(Shell.java:1634)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:4025)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:1927)
at org.eclipse.swt.widgets.Shell.callWindowProc(Shell.java:443)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:3334)
at org.eclipse.swt.widgets.Decorations.windowProc(Decorations.java:1539)
at org.eclipse.swt.widgets.Shell.windowProc(Shell.java:1634)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:4025)
at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:1932)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2966)
at org.eclipse.jface.window.Window.runEventLoop(Window.java:820)
at org.eclipse.jface.window.Window.open(Window.java:796)
at de.annabamp.adressbuch.gui.Applikation.<init>(Applikation.java:22)
at de.annabamp.adressbuch.gui.Applikation.main(Applikation.java:110)​
Nicht einmal ein einfaches "e.doit = false;" verhindert das Schließen. Es gibt zwar keinen Fehler, aber das Schließen wird nicht verhindert. Ich habe auch andere Listener, wie den DisposeListener, getestet, aber da kann ich leider das Schließen des Programms nicht verhindern.

Bei Swing würde ich einfach die Methode "windowClosed(WindowEvent e)" überschreiben und sie meinem JFrame zum fressen geben, aber hier weiß ich echt nicht weiter.

Vielen Dank für jede ernst gemeinte Antwort!

Gruß ryoshi

P.S: Ich habe versucht den Code so gut wie mir es mir möglich war zu formatieren um ihn lesbar zu machen, da ich irgendwie keine Codebox gefunden habe.
 
Hey...

zum Thema kann ich nicht unbedingt viel sagen, da ich nur Swing programmiere,
hat es in SWT nicht auch eine JOptionPane? Diese hat die Möglichkeit showConfirmDialog( );

Beispiel:
Java:
int i;

i = JOptionPane.showConfirmDialog(this, "Programm beenden?", "Beenden", JOptionPane.YES_NO_OPTION);
        if(i==0)
            System.exit(0);

für den Javacode benutzt du [ java ] Code [/ java ]

Hoffe ich konnte ein wenig helfen!
Gruss
 
Danke für deine Antwort!

Wie du vielleicht gesehen hast gibt es auch in SWT die Standartdialoge mit:
Java:
final MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
messageBox.setMessage("Möchten Sie das Programm wirklich schließen?");
messageBox.setText("Frage");
int i = messageBox.open();
Für JFace sieht das ganze so aus:
Java:
boolean beenden_Dialog = MessageDialog.openQuestion(shell, "Frage", "Wirklich Benden?");
Da liegt auch eigentlich nicht mein Problem. Wenn ich wie du Swing programmieren würde, dann wäre das ja kein Problem. Ich müsste nur das Schließen des Fensters selbst übernehmen indem ich dem JFrame sage: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); und dann addWindowListener(new WindowListener());.
Dann könnte ich über windowClosed(WindowEvent e) das mit dem Dialog selbst übernehmen und es auch verhindern.

Bei meiner JFace Anwendung habe ich aber irgendwie das Problem, dass ich dem Dialog seinen "Parent", eben die Shell, übergeben muss und diese scheint schon "disposed" zu sein, wenn er shellClosed(ShellEvent e) ausführt.

Ich könnte das ganze mit Swing realisieren, da ich dort schon sehr viel Erfahrung habe, jedoch mag ich die Elemente dort nich so sehr. Ich finde die OS-Abhängigen Stilelemente besser und AWT leistet einfach zu wenig (Keine Bäume etc.).

Vielleicht hat ja jemand anderes noch einen Vorschlag?

Gruß ryoshi
 
Hallo ryoshi,

in der JFace-Variante versuchst Du den MessageDialog direkt in der Listener-Methode zu kreieren über die statische Methode openQuestion(). Dort übergibst Du die Shell als parent, die aber offensichtlich zu dem Zeitpunkt bereits null ist, aber das hast Du ja selbst schon festgestellt. Wie wäre es, wenn Du versuchst den MessageDialog vor der Listener-Methode zu instanzieeren (über den Konstruktor) und dann im Listener nur seine open() Methode aufzurufen?

Ist nur eine Idee, kann's grad selber nicht testen...

Wenn das nichts bringt, dann würde ich es mit der MessageBox probieren, wie in Deiner ersten Variante.

Grüße
Vincent
 
Hallo ryoshi,

Du mußt das e.doit= false gleich ganz zu anfang setzen. Hatte ähnliches Problem. Wenn Du erst abfragst, dann ist das Widget schon disposed und es kommt zur Exception.

Bei mir sieht es so aus:
Code:
public void shellClosed(ShellEvent e)
      {
        e.doit = false;
        
        MessageBox box = new MessageBox(shell, SWT.YES | SWT.NO);
		box.setText("Information");
		box.setMessage("sure to exit?");
		if (box.open() == SWT.YES)
		{
		  e.doit = true;
		}        
      }


Hier mal das Bsp von Thomas, welches er in meinem Post hier im Board veröffentlicht hat.
Code:
/*
 * Created on 27.04.2005@13:24:45 by Darimont
 *
 * TODO Licence info
 */


import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;

/**
 * @author Darimont
 * 
 * TODO Explain me
 */
public class SWTCloseExample {

	static Thread runner = new Thread() {
		public void run() {
			for (int i = 0; i < 100; i++) {
				try {
					sleep(100L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	};

	public static void main(String[] args) {
		Display display = new Display();
		final Shell shell = new Shell(display);

		shell.setText("SWTCloseExample");
		shell.open();

		runner.start();

		shell.addShellListener(new ShellAdapter() {
			public void shellClosed(ShellEvent e) {
				if (runner.isAlive()) {
					e.doit = false;

					MessageBox box = new MessageBox(shell, SWT.ICON_INFORMATION);
					box.setText("Information");
					box
							.setMessage("Some *very* importent processes have not been finished yet...!\nTry again later...");
					box.open();
				}
			}
		});

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}
}


Hoffe das hilft Dir weiter.
 
Danke für die Vorschläge!

@Vincentius:
Du hast absolut recht. Wenn ich den MessageDialog vorher erstelle und dann einfach nur per open() aufrufe ist schonmal die Exception weg. Das ganze sieht dann so aus:
Java:
String[] s = {"Ja", "Nein"};
		final MessageDialog exit = new MessageDialog(getShell(), "Frage", null, "Wirklich beenden?", MessageDialog.QUESTION, s, 0);
		
		mantel.addShellListener(new ShellAdapter()
		{
			public void shellClosed(ShellEvent e)
			{
				e.doit = false;
				if(exit.open() == SWT.YES)
					e.doit = true;
			}
		});
Das Problem ist nur, dass er die Shell disposed und dann erst den Dialog anzeigt. Also irgendwie kann ich die Shell nicht dazu bewegen sich nicht zu disposen. Das doit hat absolut keine auswirkung auf das Event. Er macht es einfach.
D.H.: Er macht die Shell zu, öffnet dann den Dialog und ich kann wählen was natürlich keinen Sinn mehr hat, da die Shell schon zu ist.

@Ronin-Jay:
Bei deinem ersten Beispiel ist es wie bei mir ganz oben: Das doit tut nix und die Exception kommt, weil die Shell schon weg ist, mit der du versuchst den Dialog zu öffnen.

Bei dem zweiten Beispiel geht das natürlich. Da wird aber auch nicht mit JFace sondern nur mit SWT gearbeitet. Wenn ich nur SWT benutze, also mit Display (was ja fast komplett wegfällt bei JFace) und mit Shell dann geht auch das doit & sein ShellListener, wie dort im Beispiel (und auch ohne Thread).

Ich suche also konkret jetzt noch etwas womit ich das ShellEvent abbrechen kann und zwar auf basis eines ApplicationWindow von JFace.

Gruß ryoshi
 
Ich habe grade was in der API-Docu gefunden und zwar bei org.eclipse.jface.window.Window gibt's eine protected Methode handleShellCloseEvent(), die wohl vor dem eigentlichen "closen" aufgerufen wird. Also wenn Du Dein ApplicationWindow überschreibst, kannst Du Dich dort an die Methode anhängen und eventuell das Schliessen der Shell verhindern.

Grüße
Vincent
 
Ich danke dir vielmals Vincentius!
Genau das ist es was ich gesucht habe. Dort kann ich jetzt ohne Probleme den MessageDialog öffnen.

Ich muss gestehen das ich nur in ApplicationWindow (in der API) geschaut habe und die geerbten Methoden von Window nicht beachtet habe bzw. diese Methode schlichtweg überlesen habe.

Also für alle die das gleiche Problem haben und nach einer Lösung suchen:
Java:
public class Applikation extends ApplicationWindow
{
    public Applikation()
    {
        super(null);
        setBlockOnOpen(true);
        open();
    }

    protected void handleShellCloseEvent()
    {
        boolean exit = MessageDialog.openQuestion(getShell(), "Frage", "Wirklich beenden?");
        if(exit)
            getShell().dispose();
    }

    public static void main(String[] args)
    {
        new Applikation();
        Display.getCurrent().dispose();
    }
}
Ich arbeite noch nich lange mit SWT, daher hoffe ich mal das jetzt alles korrekt wieder freigegeben wurde.

Besten Dank an alle!
Thema erledigt, Haken gesetzt!

Gruß ryoshi
 
Zurück