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?
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.
privatevoidForm1_FormClosing(object sender,FormClosingEventArgs e){if(string.Equals((sender asButton).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.
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:
voidForm_FormClosing(object sender,FormClosingEventArgs e){if(e.CloseReason==CloseReason.UserClosing)// Prompt user to save his dataif(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 AltF4, etc. Tudo termina em um CloseReason.UserClosingmotivo.
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.
publicForm1(){InitializeComponent();this.FormClosing+=Form1_FormClosing;}/// <summary>/// Override the Close Form event/// Do something/// </summary>/// <param name="sender"></param>/// <param name="e"></param>privatevoidForm1_FormClosing(Object sender,FormClosingEventArgs e){//In case windows is trying to shut down, don't hold the process upif(e.CloseReason==CloseReason.WindowsShutDown)return;if(this.DialogResult==DialogResult.Cancel){// Assume that X has been clicked and act accordingly.// Confirm user wants to closeswitch(MessageBox.Show(this,"Are you sure?","Do you still want ... ?",MessageBoxButtons.YesNo,MessageBoxIcon.Question)){//Stay on this formcaseDialogResult.No:
e.Cancel=true;break;default:break;}}}
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
publicboolClosedByXButtonOrAltF4{get;privateset;}privateconstint SC_CLOSE =0xF060;privateconstint WM_SYSCOMMAND =0x0112;protectedoverridevoidWndProc(refMessage msg){if(msg.Msg== WM_SYSCOMMAND && msg.WParam.ToInt32()== SC_CLOSE)ClosedByXButtonOrAltF4=true;base.WndProc(ref msg);}protectedoverridevoidOnShown(EventArgs e){ClosedByXButtonOrAltF4=false;}protectedoverridevoidOnFormClosing(FormClosingEventArgs e){if(ClosedByXButtonOrAltF4)MessageBox.Show("Closed by X or Alt+F4");elseMessageBox.Show("Closed by calling Close()");}
Exemplo 2 - Verificando StackTrace
protectedoverridevoidOnFormClosing(FormClosingEventArgs e){if(newStackTrace().GetFrames().Any(x => x.GetMethod().Name=="Close"))MessageBox.Show("Closed by calling Close()");elseMessageBox.Show("Closed by X or Alt+F4");}
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).
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{publicpartialclass frmRexcel :Form{privatestring 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>privatevoid btnExit_Click(object sender,EventArgs e){this.Close();}/// <summary>/// Handles the Form Closing event/// /// </summary>/// <param name="sender"></param>/// <param name="e"></param>privatevoid frmRexcel_FormClosing(object sender,FormClosingEventArgs e){// ---- If windows is shutting down, // ---- I don't want to hold up the processif(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 applicationswitch(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 applicationcaseDialogResult.No:
e.Cancel=true;break;default: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
namespace Test{publicpartialclassMember:Form{publicMember(){InitializeComponent();}privatebool xClicked =true;privatevoid btnClose_Click(object sender,EventArgs e){
xClicked =false;Close();}privatevoidMember_FormClosing(object sender,FormClosingEventArgs e){if(xClicked){// user click the X}else{// user click the close button}}}}
Eu concordo com a DialogResult-Solution como a mais direta.
No VB.NET, no entanto, o typecast é necessário para obter o CloseReason-Property
PrivateSubMyForm_Closing(sender AsObject, e AsCancelEventArgs)HandlesMe.ClosingDim eCast AsSystem.Windows.Forms.FormClosingEventArgs
eCast =TryCast(e,System.Windows.Forms.FormClosingEventArgs)If eCast.CloseReason=Windows.Forms.CloseReason.NoneThenMsgBox("Button Pressed")ElseMsgBox("ALT+F4 or [x] or other reason")EndIfEndSub
privatevoidFrmMain_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;privatevoidMainPage_FormClosing(object sender,FormClosingEventArgs e){// your code after the form is closed}
privatevoidForm_FormClosing(object sender,FormClosingEventArgs e){if((sender asForm).ActiveControlisButton){//CloseButton}else{//The X has been clicked}}
Respostas:
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.
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
Em nosso exemplo, isso seria traduzido como
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?
fonte
A
CloseReason
enumeraçã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:
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.UserClosing
motivo.fonte
O botão "X" registra,
DialogResult.Cancel
portanto, outra opção é avaliar oDialogResult
.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
)fonte
DialogResult
permaneceNone
. Qual pode ser o problema?X
marcasDialogResult
contêmCancel
, nãoNone
. AtribuirNone
ao seu botão é o mesmo que não definir sua.DialogResult
propriedade e, se você chamar aform.Close()
partir do manipulador de eventos do botão,form.DialogResult
conteráCancel
. Apenas atribuir um valor diferente deNone
ouCancel
para todos os botões de fechamento de formulário permitirá que você faça a distinção desejada.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
Close
método, você pode usar uma das seguintes opções:WM_SYSCOMMAND
verifiqueSC_CLOSE
e defina um sinalizador.StackTrace
se algum dos quadros contémClose
chamada de método.Exemplo 1 - Alça
WM_SYSCOMMAND
Exemplo 2 - Verificando StackTrace
fonte
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).
fonte
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:
fonte
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
fonte
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
fonte
fonte
Eu concordo com a
DialogResult
-Solution como a mais direta.No VB.NET, no entanto, o typecast é necessário para obter o
CloseReason
-Propertyfonte
Também tive que registrar a função de fechamento dentro do método "InitializeComponent ()" do formulário:
Minha função "FormClosing" é semelhante à resposta fornecida ( https://stackoverflow.com/a/2683846/3323790 ):
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:
fonte
Eu fiz algo assim.
fonte