C# OnFormClosing和e.CloseReason

C# OnFormClosing和e.CloseReason,c#,winforms,C#,Winforms,我有一个表单,上面有两个按钮“取消”和“确定”。在“Cancel”按钮的处理程序中,我执行需要执行的回滚操作,然后调用this.Close()。在“OK”按钮的处理程序中,我执行所有需要执行的操作,然后调用this.Close() 因此,我现在意识到,用户也可能单击表单右上角的“X”图标来关闭表单。我应该像对待“取消”按钮一样对待它,但我没有办法覆盖“X”按钮。我能做的最好的事情就是为OnFormClosing事件添加一个处理程序 但我仍然感到困惑,不确定处理这个问题的最佳方式是什么。表单也可以

我有一个表单,上面有两个按钮“取消”和“确定”。在“Cancel”按钮的处理程序中,我执行需要执行的回滚操作,然后调用
this.Close()
。在“OK”按钮的处理程序中,我执行所有需要执行的操作,然后调用
this.Close()

因此,我现在意识到,用户也可能单击表单右上角的“X”图标来关闭表单。我应该像对待“取消”按钮一样对待它,但我没有办法覆盖“X”按钮。我能做的最好的事情就是为
OnFormClosing
事件添加一个处理程序

但我仍然感到困惑,不确定处理这个问题的最佳方式是什么。表单也可以关闭还有许多其他原因(如ALT-F4或Windows关闭),我希望将所有这些都视为“取消”按钮

但是,无论我是通过单击“X”、“close”(最终调用
this.close()
)还是“OK”(最终调用
this.close()
)来关闭表单,
e.CloseReason
的值是相同的(
UserClosing
),而
sender
的值也是相同的,因此我无法区分


如果表单因单击“确定”按钮以外的任何原因关闭,实现回滚的最佳方法是什么?

在某些表单上,我通过使用Win32函数隐藏X按钮来解决问题-不知道它是否适合您的情况,但代码如下

/// <summary>
/// Win32 function to get window information
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="nIndex">Information to get</param>
/// <returns>Required Window Information</returns>
[DllImport("user32.dll")]
internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
/// <summary>
/// Code to get/set Style for window
/// </summary>
private const int GWL_STYLE = -16;
/// <summary>
/// System menu bit
/// </summary>
private const int WS_SYSMENU = 0x80000;

/// <summary>
/// Hide close button on window
/// </summary>
/// <param name="w">Window to set</param>
/// <remarks>Actually removes all of system menu from window title bar</remarks>
public static void HideCloseButton(Window w)
{
  var hwnd = new WindowInteropHelper(w).Handle;
  NativeMethods.SetWindowLong(hwnd, GWL_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
/// <summary>
/// Show close button on window
/// </summary>
/// <param name="w">Window to set</param>
/// <remarks>Actually shows all of system menu from window title bar</remarks>
public static void ShowCloseButton(Window w)
{
  var hwnd = new WindowInteropHelper(w).Handle;
  NativeMethods.SetWindowLong(hwnd, GWL_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
//
///获取窗口信息的Win32函数
/// 
///窗把手
///要获取的信息
///所需窗口信息
[DllImport(“user32.dll”)]
内部静态外部int GetWindowLong(IntPtr hWnd,int nIndex);
/// 
///获取/设置窗口样式的代码
/// 
私有常量int GWL_STYLE=-16;
/// 
///系统菜单位
/// 
私有常量int WS_SYSMENU=0x80000;
/// 
///隐藏窗口上的关闭按钮
/// 
///设置窗口
///实际上从窗口标题栏中删除所有系统菜单
公共静态无效隐藏关闭按钮(窗口w)
{
var hwnd=新的WindowInteropHelper(w).Handle;
NativeMethods.SetWindowLong(hwnd,GWL_样式,NativeMethods.GetWindowLong(hwnd,GWL_样式)&~WS_系统菜单);
}
/// 
///在窗口上显示关闭按钮
/// 
///设置窗口
///实际显示窗口标题栏中的所有系统菜单
公共静态无效显示关闭按钮(窗口w)
{
var hwnd=新的WindowInteropHelper(w).Handle;
NativeMethods.SetWindowLong(hwnd,GWL_样式,NativeMethods.GetWindowLong(hwnd,GWL_样式)| WS_系统菜单);
}

您只需在OK按钮处理程序中设置一个布尔值,然后在OnFormClosing()覆盖中检查该布尔值,如下所示:

public partial class Form1: Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        base.OnFormClosing(e);

        if (!this.isClosingViaOkButton)
        {
            // ...do your rollback here.

            MessageBox.Show("Rolling back");
        }
    }

    private void okButton_Click(object sender, EventArgs e)
    {
        // ...do your committing here.

        this.isClosingViaOkButton = true;
        this.Close();
    }

    private bool isClosingViaOkButton;
}
作为替代方案,您可以在OnFormClosing()中执行这两项操作,而不是在OK按钮处理程序中提交并在OnFormClosing()中回滚,如下所示:

protected override void OnFormClosing(FormClosingEventArgs e)
{
    base.OnFormClosing(e);

    if (this.isClosingViaOkButton)
    {
        // ...do your committing.
    }
    else
    {
        // ...do your rollback.
    }
}

我自己更喜欢这种方法。

您真的不需要
CloseReason
,是吗

private bool _closing;
private void Form1_FormClosing( object sender, FormClosingEventArgs e )
{
    e.Cancel = !_closing; // prevent form from being closed
}

// inside OK and Cancel button handler
...
_closing = true;
Close();

请阅读DialogResult属性。如果要创建自定义原因信息,只需在表单中创建其他属性。这不是对话框,而是使用form.Show()显示的普通表单。DialogResult是没有意义的。我在window loaded事件中调用它:private void window_loaded(object sender,RoutedEventArgs e){LibW2.HideCloseButton(this);}谢谢,但我不希望“x”按钮不可见。这将破坏我的应用程序的一致性。这似乎是在回避问题,而不是解决问题。