Làm thế nào để biết người dùng đã nhấp vào “X” hoặc nút “Đóng”?


94

Trong MSDN, tôi CloseReason.UserClosingbiết rằng người dùng đã quyết định đóng biểu mẫu nhưng tôi đoán rằng việc nhấp vào nút X hoặc nhấp vào nút đóng đều giống nhau. Vì vậy, làm thế nào tôi có thể phân biệt giữa hai điều này trong mã của tôi?

Cảm ơn tất cả.


2
ý bạn là nút đóng nào?
Brian R. Bondy

ví dụ: đóng cửa bằng "ALT + F4"
Bohn


@Oliver Không phải câu hỏi tương tự.
Ctrl S

Câu trả lời:


95

Giả sử bạn đang yêu cầu WinForms, bạn có thể sử dụng sự kiện FormClosing () . Sự kiện FormClosing () được kích hoạt bất cứ lúc nào biểu mẫu được đóng.

Để phát hiện xem người dùng có nhấp vào X hoặc CloseButton của bạn hay không, bạn có thể lấy nó thông qua đối tượng người gửi. Cố gắng truyền người gửi làm điều khiển Nút và xác minh có lẽ cho tên của nó là "CloseButton", chẳng hạn.

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.
}

Nếu không, tôi chưa bao giờ cần phải phân biệt xem X hay CloseButton đã được nhấp vào hay chưa, vì tôi muốn thực hiện một cái gì đó cụ thể trên sự kiện FormClosing, chẳng hạn như đóng tất cả MdiChildren trước khi đóng MDIContainerForm hoặc kiểm tra sự kiện để tìm các thay đổi chưa được lưu. Trong những trường hợp này, theo tôi, chúng ta không cần phải phân biệt với một trong hai nút.

Đóng bằng ALT+ F4cũng sẽ kích hoạt sự kiện FormClosing (), vì nó sẽ gửi một thông báo đến Biểu mẫu có nội dung là đóng. Bạn có thể hủy sự kiện bằng cách đặt

FormClosingEventArgs.Cancel = true. 

Trong ví dụ của chúng tôi, điều này sẽ dịch là

e.Cancel = true.

Lưu ý sự khác biệt giữa các sự kiện FormClosing () và FormClosed () .

FormClosing xảy ra khi biểu mẫu nhận được thông báo cần đóng và xác minh xem nó có việc gì cần làm trước khi đóng hay không.

FormClosed xảy ra khi biểu mẫu thực sự được đóng, vì vậy sau khi nó được đóng.

Không giúp đỡ à?


Vâng, cảm ơn cho ý tưởng "Diễn viên", đã sử dụng kỹ thuật này với Delphi 7 nhưng quên làm điều tương tự trong C #
Bohn

Trên thực tế, đây là một cổng từ Delphi sang .NET. =) Tôi rất vui vì tôi đã giúp!
Will Marcouiller

1
Tôi nhận được 'Tham chiếu đối tượng không được đặt thành một phiên bản của đối tượng' khi tôi sử dụng mã này.
Nate S.

33
Cái này sai. Bạn không thể truyền người gửi tới nút vì đó là biểu mẫu. Điều này ném ngoại lệ.
Xtro

1
Xin lưu ý rằng đây là câu trả lời KHÔNG ĐÚNG. Vui lòng không ủng hộ điều này.
Najeeb

79

Các CloseReason liệt kê bạn tìm thấy trên MSDN chỉ nhằm mục đích kiểm tra xem người dùng đã đóng ứng dụng hay là do tắt máy hoặc do trình quản lý tác vụ đóng, v.v.

Bạn có thể thực hiện các hành động khác nhau, tùy theo lý do, như:

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
}

Nhưng giống như bạn đoán, không có sự khác biệt giữa việc nhấp vào nút x, hoặc nhấp chuột phải vào thanh tác vụ và nhấp vào 'đóng' hoặc nhấn Alt F4, v.v. Tất cả đều có CloseReason.UserClosinglý do.


11
Sử dụng tiêu chuẩn Close (); dường như kích hoạt CloseReason.UserClosing cho tôi. Không chắc chắn lý do tại sao.
Dan W

Tôi thấy điều này hữu ích khi cố gắng chặn việc đóng biểu mẫu con MDI bằng hành động của người dùng trên biểu mẫu nhưng không phải khi đóng biểu mẫu gốc.
Steve Pettifer

1
Điều này không trả lời câu hỏi, chỉ liệt kê thêm vấn đề.
Robert Koernke

Làm thế nào để bạn liên kết sự kiện với phương thức?
user2924019

43

Nút "X" đăng ký vì DialogResult.Cancelvậy một tùy chọn khác là đánh giá DialogResult.

Nếu bạn có nhiều nút trên biểu mẫu của mình, có thể bạn đã liên kết các DialogResultnút khác nhau với từng nút và điều này sẽ cung cấp cho bạn phương tiện để phân biệt sự khác biệt giữa từng nút.

(Ví dụ: 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
Trong trường hợp của tôi, điều này hữu ích hơn câu trả lời được chấp nhận. Vì 'X' được gán DialogResult.Cancel, việc gán một số giá trị khác cho nút hủy sẽ dễ dàng phân biệt chúng và xử lý mọi thứ một cách thích hợp.
MickeyfAgain_BeforeExitOfSO

3
Điều này không hoạt động trong trường hợp của tôi. Khi nhấn 'X', DialogResultvẫn còn None. Điều gì có thể là vấn đề?
Bhaskar

1
@Bhaskar, khi bạn khởi tạo hộp thoại của mình, hãy đảm bảo đặt DialogResult thích hợp cho từng nút trong hộp thoại của bạn. Tôi đã cung cấp một ví dụ ở trên nhưng không tạo khối mã để hiển thị khai báo Hộp thoại.
AlexScript

@Bhaskar: Nhấn Xlàm cho DialogResultchứa Cancel, không None. Việc gán Nonecho nút của bạn cũng giống như hoàn toàn không thiết lập thuộc tính của nó .DialogResultvà nếu bạn gọi form.Close()từ trình xử lý sự kiện của nút, form.DialogResultsẽ chứa Cancel. Chỉ chỉ định một giá trị khác Nonehoặc Cancelcho tất cả các nút đóng biểu mẫu của bạn sẽ cho phép bạn tạo ra sự khác biệt mong muốn.
mklement0

9

Làm thế nào để phát hiện nếu biểu mẫu đã đóng bằng cách nhấp vào nút X hoặc bằng cách gọi Close()bằng mã?

Bạn không thể dựa vào lý do đóng của chuỗi sự kiện đóng biểu mẫu, bởi vì nếu người dùng nhấp vào nút X trên thanh tiêu đề hoặc đóng biểu mẫu bằng Alt + F4 hoặc sử dụng menu hệ thống để đóng biểu mẫu hoặc biểu mẫu được đóng bằng cách gọi Close(), tất cả các trường hợp trên, lý do đóng sẽ do Người dùng đóng mà không phải là kết quả mong muốn.

Để phân biệt biểu mẫu được đóng bằng nút X hay bằng Closephương pháp, bạn có thể sử dụng một trong các tùy chọn sau:

  • Xử lý WM_SYSCOMMANDvà kiểm tra SC_CLOSEvà đặt cờ.
  • Kiểm tra StackTracexem có bất kỳ khung nào chứa Closelệnh gọi phương thức hay không.

Ví dụ 1 - Xử lý 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()");
}

Ví dụ 2 - Kiểm tra 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
Tốt lắm. Thật tệ là bạn đã đến bữa tiệc muộn - khó có thể cạnh tranh với những câu trả lời cũ hơn, đã được bình chọn cao.
mklement0

1
@ mklement0 Tôi hy vọng những người dùng trong tương lai thấy nó hữu ích. Tôi đã đăng câu trả lời vì không phải câu trả lời nào khác có thể giải quyết vấn đề một cách chính xác và khá lạ cho một câu hỏi có số lượt xem và câu trả lời được bình chọn cao (không hoạt động)!
Reza Aghaei

7

Nó xác định thời điểm đóng đơn nếu một biểu mẫu được đóng (nếu đơn của bạn không được đính kèm với một biểu mẫu cụ thể).

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

5

Tôi luôn sử dụng phương thức Đóng biểu mẫu trong các ứng dụng của mình để bắt alt + x từ Nút thoát của tôi, alt + f4 hoặc sự kiện đóng biểu mẫu khác đã được bắt đầu. Tất cả các lớp của tôi đều có tên lớp được định nghĩa là Chuỗi riêng mstrClsTitle = "grmRexcel"trong trường hợp này, một phương thức Exit gọi Phương thức đóng biểu mẫu và một Phương thức đóng biểu mẫu. Tôi cũng có một tuyên bố cho Phương pháp đóng biểu mẫu - this.FormClosing = My Form Closing Form Closing method name.

Mã cho điều này:

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

Bạn có thể thử thêm trình xử lý sự kiện từ thiết kế như sau: Mở biểu mẫu trong dạng xem thiết kế, mở cửa sổ thuộc tính hoặc nhấn F4, nhấp vào nút thanh công cụ sự kiện để xem các sự kiện trên đối tượng Biểu mẫu, tìm sự kiện FormClosing trong nhóm Hành vi và nhấp đúp vào nó. Tham khảo: 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;
            }
        }

nếu điều kiện sẽ thực thi khi người dùng nhấp vào 'X' hoặc nút đóng trên biểu mẫu. Cái khác có thể được sử dụng khi người dùng nhấp vào Alt + f4 cho bất kỳ mục đích nào khác


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

Tôi đồng ý với DialogResult-Giải pháp là giải pháp thẳng về phía trước hơn.

Tuy nhiên, trong VB.NET, cần có typecast để lấy 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

Tôi cũng phải đăng ký hàm đóng bên trong phương thức "InitializeComponent ()" của biểu mẫu:

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

Hàm "FormClosing" của tôi trông giống với câu trả lời đã cho ( 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");
    }
}

Một điều nữa cần đề cập: Ngoài ra còn có một chức năng "FormClosed" xuất hiện sau "FormClosing". Để sử dụng chức năng này, hãy đăng ký nó như hình dưới đây:

this.FormClosed += MainPage_FormClosed;

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

0

Tôi đã làm một cái gì đó như thế này.

private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
        if ((sender as Form).ActiveControl is Button)
        {
            //CloseButton
        }
        else
        {
            //The X has been clicked
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.