Как Swing WindowListener может наложить вето на JFrame?

У меня есть рамка, и я хочу подсказать, когда пользователь закрывает ее, чтобы сохранить документ. Но если они отменяются, кадр не должен закрываться.

frame.addWindowListener(new SaveOnCloseWindowListener(fileState));
...
public class SaveOnCloseWindowListener extends WindowAdapter {
    private final FileState fileState;

    public SaveOnCloseWindowListener(FileState fileState) {
        this.fileState = fileState;
    }

    public void windowClosing(WindowEvent e) {
        if (!fileState.onQuit())
            cancelClose();  
    }
}

FileState проверяет, является ли документ грязным. Если это не так, он ничего не делает и возвращает истину. Если он грязный, он спрашивает пользователя, хочет ли он сохранить (ДА / НЕТ / ОТМЕНА). Если пользователь отменяет в этот момент, он должен прервать закрытие окна.

Все предложения, которые я видел в сети, включают в себя явный выход из метода windowClosing, таким образом переопределяя использование JFrame.setDefaultCloseOperation () и дублируя код в JFrame.processWindowEvent ().

У меня действительно есть грязное решение, но я хотел бы видеть, есть ли более чистые решения.

ура

 Andrew Thompson17 мар. 2013 г., 01:16

Ответы на вопрос(4)

Я думаю, что это логическое выражение ответа Тирлера и то, чего я пытался избежать.

frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new SaveOnCloseWindowListener(fileState, JFrame.EXIT_ON_CLOSE));

public class SaveOnCloseWindowListener extends WindowAdapter {

    private final int closeOperation;
    private final Document document;

    public SaveOnCloseWindowListener(int closeOperation, Document document) {
        this.closeOperation = closeOperation;
        this.document = document;
    }

    public void windowClosing(WindowEvent e) {
        if (!document.onClose())
            doClose((Window) e.getSource());
    }

    private void doClose(Window w) {
        switch (closeOperation) {
        case JFrame.HIDE_ON_CLOSE:
            w.setVisible(false);
            break;
        case JFrame.DISPOSE_ON_CLOSE:
            w.dispose();
            break;
        case JFrame.DO_NOTHING_ON_CLOSE:
        default:
            break;
        case JFrame.EXIT_ON_CLOSE:
            System.exit(0);
            break;
        }
    }
}
 Duncan McGregor23 сент. 2010 г., 14:34
Причина, по которой мне это не нравится, заключается в том, что он дублирует код в JFrame и требует, чтобы подкласс фрейма знал, что такое требуемая операция, когда он создает прослушиватель окна. Я полагаю, я мог бы переопределить setDefaultCloseOperation, чтобы передать операцию слушателю ...
 Thirler23 сент. 2010 г., 14:35
Я отредактирую свой ответ с решением, которое я имею в виду. В общем, вы тот, кто создает JFrame, поэтому вы знаете, какую операцию закрытия вы хотите, по сути, вы отключаете операцию закрытия.
 Duncan McGregor24 сент. 2010 г., 18:28
Я думаю, что в этом мы и отличаемся. Мне нужен JFrame, который может наложить вето на закрытие, если пользователь говорит, что не хочет закрывать его, но где операция закрытия по умолчанию - это политика приложения, а не фрейма.

Закрытие заявки может сделать процесс немного проще для вас.

 Duncan McGregor24 сент. 2010 г., 19:04
Я должен признаться, что я не нажал на ваш код. Теперь, когда у меня есть - ExitAction фактически не завершается, он закрывает активный кадр, который может или не может выйти из приложения.
 camickr24 сент. 2010 г., 18:56
Я думал, что дал элегантное решение. Он управляет «подсказками» и «закрытием» для вас. Я полагаю, я не понимаю, что вас беспокоит.
 Duncan McGregor24 сент. 2010 г., 19:06
И я не могу решить, нравится ли мне CloseListener манипулировать defaultCloseOperation на лету. Мне кажется, что это неправильно, но я вижу, что это делает работу. Так что спасибо, я постараюсь быть немного менее добрым ;-)
 Duncan McGregor24 сент. 2010 г., 18:20
Спасибо, но я понимаю, как все это работает (я программировал на Swing с момента импорта com.sun.java.swing. *). Я пытаюсь найти элегантный способ решения проблемы.
Решение Вопроса

Правильный путь установленJFrame.setDefaultCloseOperation вDO_NOTHING_ON_CLOSE когда окно создано. А потом просто звонюsetVisible(false) или жеdispose() когда ваш пользователь принимает закрытие или ничего не делает, когда закрытие не принято.

Вся цельJFrame.setDefaultCloseOperation только для предотвращения необходимости реализацииWindowListeners для самых простых действий. Действия, выполняемые этими операциями закрытия по умолчанию, очень просты.

РЕДАКТИРОВАТЬ:

Я добавил решение, которое я описываю. Это предполагает, что вы хотите, чтобы кадр был полностью удален.

frame.setDefaultCloseOperation(setDefaultCloseOperation);
frame.addWindowListener(new SaveOnCloseWindowListener(fileState));
...

public class SaveOnCloseWindowListener extends WindowAdapter {
    private final FileState fileState;

    public SaveOnCloseWindowListener(FileState fileState) {
        this.fileState = fileState;
    }

    public void windowClosing(WindowEvent e) {
        if (fileState.onQuit())
            frame.dispose();
    }
}
 Duncan McGregor23 сент. 2010 г., 14:06
Действительно - я отредактировал вопрос, чтобы сделать это более ясным, я надеюсь.
 Duncan McGregor24 сент. 2010 г., 18:24
Читая ваш ответ, мне нравится: «Вся цель JFrame.setDefaultCloseOperation заключается только в том, чтобы предотвратить необходимость реализации WindowListeners для самых простых действий». Это было бы намного проще выразить, если бы JFrame processWindowEvent делегировал processCloseOperation, которое я мог бы вызвать.
 Thirler23 сент. 2010 г., 14:29
На самом деле мой ответ все еще применяется тогда. Единственное изменение, необходимое для вашего данного примера, это то, что вам нужно позвонитьsetDefaultCloseOperation на создание, чтобы помешать ему что-либо делать. И вместо отмены закрытия вы явно закрываете окно, когда считаете, что это необходимо.
 Duncan McGregor23 сент. 2010 г., 13:46
Но тогда мой слушатель должен был бы знать, что это за операция. Я полагаю, что мог бы передать ему операцию закрытия по умолчанию и продублировать код в processWindowEvent, но, похоже, это позор.
 Thirler23 сент. 2010 г., 13:51
Если вы имеете в виду желаемую операцию пользователем, то да. В слушателе вы должны были бы открыть диалоговое окно. Поэтому, если я правильно понимаю, что вы хотите, вы: вы не должны спрашивать, чего хочет пользователь, пока не получите событие закрытия.
 Duncan McGregor28 сент. 2010 г., 17:47
Я принял этот ответ, чтобы воздать должное Тирлеру, и чтобы этот вопрос не оставался открытым. Лично я остаюсь со своим уродливым хакерским исключением, вы можете выбрать 2.

Благодаря вкладу Тирлера и Камикра. Это мое решение, которое, я уверен, некоторые будут ненавидеть, но избегает дублирования логики в JFrame и позволяет клиентскому коду устанавливать setDefaultCloseOperation как обычно.

class MyFrame {        
    @Override protected void processWindowEvent(WindowEvent e) {
        try {
            super.processWindowEvent(e);
        } catch (SaveOnCloseWindowListener.VetoException x) {}
    }
}

public class SaveOnCloseWindowListener extends WindowAdapter {

    public static class VetoException extends RuntimeException {
    }

    private final DocumentController documentController;

    public SaveOnCloseWindowListener(DocumentController documentController) {
        this.documentController = documentController;
    }

    public void windowClosing(WindowEvent e) {
        if (!documentController.onClose())
            throw new VetoException();
    }
}

Ваш ответ на вопрос