Como saber se o usuário clicou no “X” ou no botão “Fechar”?


94

No MSDN descobri CloseReason.UserClosingque o usuário decidiu fechar o formulário, mas acho que é o mesmo para clicar no botão X ou clicar no botão Fechar. Então, como posso diferenciar entre esses dois no meu código?

Obrigado a todos.


2
que botão fechar você quer dizer?
Brian R. Bondy,

por exemplo, fechando em "ALT + F4"
Bohn


@Oliver Não é a mesma pergunta.
Ctrl S

Respostas:


95

Supondo que você esteja solicitando WinForms, você pode usar o evento FormClosing () . O evento FormClosing () é disparado sempre que um formulário deve ser fechado.

Para detectar se o usuário clicou no X ou no CloseButton, você pode obtê-lo por meio do objeto remetente. Tente lançar o remetente como um controle de Botão e verifique, talvez, por seu nome "CloseButton", por exemplo.

private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
    if (string.Equals((sender as Button).Name, @"CloseButton"))
        // Do something proper to CloseButton.
    else
        // Then assume that X has been clicked and act accordingly.
}

Caso contrário, nunca precisei diferenciar se X ou CloseButton foi clicado, pois eu queria realizar algo específico no evento FormClosing, como fechar todos os MdiChildren antes de fechar o MDIContainerForm ou verificar o evento para alterações não salvas. Nessas circunstâncias, não precisamos, na minha opinião, diferenciar um dos botões.

Fechar com ALT+ F4também acionará o evento FormClosing (), pois enviará uma mensagem ao Form que diz para fechar. Você pode cancelar o evento definindo o

FormClosingEventArgs.Cancel = true. 

Em nosso exemplo, isso seria traduzido como

e.Cancel = true.

Observe a diferença entre os eventos FormClosing () e FormClosed () .

O FormClosing ocorre quando o formulário recebe a mensagem a ser encerrada, e verifique se há algo a fazer antes de ser encerrado.

FormClosed ocorre quando o formulário é realmente fechado, ou seja, após ser fechado.

Isso ajuda?


Sim, obrigado pela ideia de "Cast", usei essa técnica com Delphi 7 mas esqueci de fazer o mesmo em C #
Bohn

Na verdade, é uma porta do Delphi para o .NET. =) Estou feliz por ter ajudado!
Will Marcouiller

1
Eu obtenho 'Referência de objeto não definida para uma instância de um objeto' quando uso este código.
Nate S.

33
Isto está errado. Você não pode lançar o remetente para o botão porque é o próprio formulário. Isso lança uma exceção.
Xtro

1
Observe que esta é uma resposta INCORRETA. Por favor, não vote a favor.
Najeeb

78

A CloseReasonenumeração que você encontrou no MSDN serve apenas para verificar se o usuário fechou o aplicativo, se foi devido a um desligamento ou pelo gerenciador de tarefas, etc ...

Você pode fazer diferentes ações, de acordo com o motivo, como:

void Form_FormClosing(object sender, FormClosingEventArgs e)
{
    if(e.CloseReason == CloseReason.UserClosing)
        // Prompt user to save his data

    if(e.CloseReason == CloseReason.WindowsShutDown)
        // Autosave and clear up ressources
}

Mas, como você adivinhou, não há diferença entre clicar no botão x, ou clicar com o botão direito na barra de tarefas e clicar em 'fechar', ou pressionar Alt F4, etc. Tudo termina em um CloseReason.UserClosingmotivo.


11
Usando o padrão Close (); parece acionar CloseReason.UserClosing para mim. Não sei por quê.
Dan W

Achei isso útil ao tentar bloquear o fechamento de um formulário filho MDI pela ação do usuário no formulário, mas não quando o pai está sendo fechado.
Steve Pettifer

1
Isso não responde à pergunta, apenas enumera ainda mais a questão.
Robert Koernke de

Como você vincula o evento ao método?
user2924019

43

O botão "X" registra, DialogResult.Cancelportanto, outra opção é avaliar o DialogResult.

Se você tiver vários botões em seu formulário, provavelmente já está associando diferentes DialogResult s a cada um e isso fornecerá os meios para saber a diferença entre cada botão.

(Exemplo: btnSubmit.DialogResult = DialogResult.OK, btnClose.DialogResult = Dialogresult.Abort)

    public Form1()
    {
        InitializeComponent();

        this.FormClosing += Form1_FormClosing;
    }

    /// <summary>
    /// Override the Close Form event
    /// Do something
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_FormClosing(Object sender, FormClosingEventArgs e)
    {
        //In case windows is trying to shut down, don't hold the process up
        if (e.CloseReason == CloseReason.WindowsShutDown) return;

        if (this.DialogResult == DialogResult.Cancel)
        {
            // Assume that X has been clicked and act accordingly.
            // Confirm user wants to close
            switch (MessageBox.Show(this, "Are you sure?", "Do you still want ... ?", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
            {
                //Stay on this form
                case DialogResult.No:
                    e.Cancel = true;
                    break;
                default:
                    break;
            }
        }
    }

1
No meu caso, isso é mais útil do que a resposta aceita. Como o 'X' é atribuído a DialogResult.Cancel, atribuir algum outro valor ao botão cancelar distingue-os facilmente e trata as coisas de maneira apropriada.
MickeyfAgain_BeforeExitOfSO

3
Isso não funciona no meu caso. Ao pressionar 'X', DialogResultpermanece None. Qual pode ser o problema?
Bhaskar de

1
@Bhaskar, ao instanciar sua caixa de diálogo, certifique-se de definir o DialogResult apropriado para cada botão em sua caixa de diálogo. Eu havia fornecido um exemplo acima, mas não criei um bloco de código para mostrar a declaração do Dialog.
AlexScript de

@Bhaskar: Pressionar Xmarcas DialogResultcontêm Cancel, não None. Atribuir Noneao seu botão é o mesmo que não definir sua .DialogResultpropriedade e, se você chamar a form.Close()partir do manipulador de eventos do botão, form.DialogResultconterá Cancel. Apenas atribuir um valor diferente de Noneou Cancelpara todos os botões de fechamento de formulário permitirá que você faça a distinção desejada.
mklement0

9

Como detectar se o formulário foi fechado clicando no botão X ou chamando o Close()código?

Você não pode confiar na razão de fechamento dos argumentos de evento de fechamento do formulário, porque se o usuário clicar no botão X na barra de título ou fechar o formulário usando Alt + F4 ou usar o menu do sistema para fechar o formulário ou o formulário será fechado chamando o Close()método, todos casos acima, o motivo de fechamento será Fechado pelo usuário, o que não é o resultado desejado.

Para distinguir se o formulário foi fechado pelo botão X ou por Closemétodo, você pode usar uma das seguintes opções:

  • Manuseie, WM_SYSCOMMANDverifique SC_CLOSEe defina um sinalizador.
  • Verifique StackTracese algum dos quadros contém Closechamada de método.

Exemplo 1 - Alça WM_SYSCOMMAND

public bool ClosedByXButtonOrAltF4 {get; private set;}
private const int SC_CLOSE = 0xF060;
private const int WM_SYSCOMMAND = 0x0112;
protected override void WndProc(ref Message msg)
{
    if (msg.Msg == WM_SYSCOMMAND && msg.WParam.ToInt32() == SC_CLOSE)
        ClosedByXButtonOrAltF4 = true;
    base.WndProc(ref msg);
}
protected override void OnShown(EventArgs e)
{
    ClosedByXButtonOrAltF4 = false;
}   
protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (ClosedByXButtonOrAltF4)
        MessageBox.Show("Closed by X or Alt+F4");
    else
        MessageBox.Show("Closed by calling Close()");
}

Exemplo 2 - Verificando StackTrace

protected override void OnFormClosing(FormClosingEventArgs e)
{
    if (new StackTrace().GetFrames().Any(x => x.GetMethod().Name == "Close"))
        MessageBox.Show("Closed by calling Close()");
    else
        MessageBox.Show("Closed by X or Alt+F4");
}

1
Bem feito. Pena que você chegou atrasado à festa - difícil competir com as respostas mais antigas e já bem votadas.
mklement0

1
@ mklement0 Espero que os futuros usuários o considerem útil. Eu postei a resposta porque nenhuma resposta da outra poderia resolver o problema corretamente e é muito estranho para uma pergunta com esse número de visualizações e respostas bem votadas (não funcionando)!
Reza Aghaei

6

Ele determina quando fechar o aplicativo se um formulário for fechado (se o seu aplicativo não estiver anexado a um formulário específico).

    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        if (Application.OpenForms.Count == 0) Application.Exit();
    }

5

Eu sempre uso um método Form Close em meus aplicativos que captura alt + x do meu botão de saída, alt + f4 ou outro evento de fechamento de formulário foi iniciado. Todas as minhas classes têm o nome da classe definido como Private string mstrClsTitle = "grmRexcel"neste caso, um método Exit que chama o Form Closing Method e um Form Closing Method. Também tenho uma declaração para o Método de fechamento de formulário - this.FormClosing = My Form Closing Form Closing method name.

O código para isso:

namespace Rexcel_II
{
    public partial class frmRexcel : Form
    {
        private string mstrClsTitle = "frmRexcel";

        public frmRexcel()
        {
            InitializeComponent();

            this.FormClosing += frmRexcel_FormClosing;
        }

        /// <summary>
        /// Handles the Button Exit Event executed by the Exit Button Click
        /// or Alt + x
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExit_Click(object sender, EventArgs e)
        {            
            this.Close();        
        }


        /// <summary>
        /// Handles the Form Closing event
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmRexcel_FormClosing(object sender, FormClosingEventArgs e)
        {

            // ---- If windows is shutting down, 
            // ---- I don't want to hold up the process
            if (e.CloseReason == CloseReason.WindowsShutDown) return;
            {

                // ---- Ok, Windows is not shutting down so
                // ---- either btnExit or Alt + x or Alt + f4 has been clicked or
                // ---- another form closing event was intiated
                //      *)  Confirm user wants to close the application
                switch (MessageBox.Show(this, 
                                    "Are you sure you want to close the Application?",
                                    mstrClsTitle + ".frmRexcel_FormClosing",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                {

                    // ---- *)  if No keep the application alive 
                    //----  *)  else close the application
                    case DialogResult.No:
                        e.Cancel = true;
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

2

Você pode tentar adicionar um manipulador de eventos a partir do design como este: Abra o formulário na visualização do design, abra a janela de propriedades ou pressione F4, clique no botão da barra de ferramentas do evento para visualizar os eventos no objeto Form, encontre o evento FormClosing no grupo Behavior e clique duas vezes nele. Referência: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9bdee708-db4b-4e46-a99c-99726fa25cfb/how-do-i-add-formclosing-event?forum=csharpgeneral


1
if (this.DialogResult == DialogResult.Cancel)
        {

        }
        else
        {
            switch (e.CloseReason)
            {
                case CloseReason.UserClosing:
                    e.Cancel = true;
                    break;
            }
        }

se a condição será executada quando o usuário clicar no 'X' ou no botão Fechar no formulário. O else pode ser usado quando o usuário clica em Alt + f4 para qualquer outra finalidade


1
namespace Test
{
    public partial class Member : Form
    {
        public Member()
        {
            InitializeComponent();
        }

        private bool xClicked = true;

        private void btnClose_Click(object sender, EventArgs e)
        {
            xClicked = false;
            Close();
        }

        private void Member_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (xClicked)
            {
                // user click the X
            } 
            else 
            {
                // user click the close button
            }
        }
    }
}

1

Eu concordo com a DialogResult-Solution como a mais direta.

No VB.NET, no entanto, o typecast é necessário para obter o CloseReason-Property

    Private Sub MyForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing

        Dim eCast As System.Windows.Forms.FormClosingEventArgs
        eCast = TryCast(e, System.Windows.Forms.FormClosingEventArgs)
        If eCast.CloseReason = Windows.Forms.CloseReason.None Then
            MsgBox("Button Pressed")
        Else
            MsgBox("ALT+F4 or [x] or other reason")
        End If

    End Sub

0

Também tive que registrar a função de fechamento dentro do método "InitializeComponent ()" do formulário:

private void InitializeComponent() {
// ...
this.FormClosing += FrmMain_FormClosing;
// ...
}

Minha função "FormClosing" é semelhante à resposta fornecida ( https://stackoverflow.com/a/2683846/3323790 ):

private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) {
    if (e.CloseReason == CloseReason.UserClosing){
        MessageBox.Show("Closed by User", "UserClosing");
    }

    if (e.CloseReason == CloseReason.WindowsShutDown){
        MessageBox.Show("Closed by Windows shutdown", "WindowsShutDown");
    }
}

Mais uma coisa a mencionar: há também uma função "FormClosed" que ocorre após "FormClosing". Para usar esta função, registre-a conforme mostrado abaixo:

this.FormClosed += MainPage_FormClosed;

private void MainPage_FormClosing(object sender, FormClosingEventArgs e)
{
// your code after the form is closed
}

0

Eu fiz algo assim.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.