【问题标题】:How to detect parent form cancelling a FormClosing event?如何检测父表单取消 FormClosing 事件?
【发布时间】:2012-07-03 06:33:43
【问题描述】:

我从主窗体打开了一个子窗体,如下所示:

var cf = new ChildForm { Owner = this };
cf.Show();

然后子窗体在另一个线程上进行一些绘图。

当用户试图关闭主窗体时 - 如果子窗体已打开 - 则首先在 ChildForm 中触发 FormClosing 事件,然后在主窗体中引发相同的事件。在FormClosing 事件中,子窗体停止其绘图线程。

当主窗体包含未保存的数据时,用户可能会尝试关闭主窗体。然后,主窗体的 FormClosing 事件处理程序向它们显示警告“数据未保存。取消关闭?”。然后他们可以取消保存(即Cancel 标志由主窗体的FormClosing 事件处理程序在FormClosingEventArgs 对象上设置)。

但是,到那时,子窗体的FormClosing 事件已经引发,并且它会停止其绘图线程。子窗体不知道它现在应该继续绘制(好像什么都没发生一样)。

是否可以从子窗体中检测到FormClosing 事件已被主窗体取消?当用户被要求在主窗体中保存数据时,我仍然想停止重绘线程。

【问题讨论】:

  • 有任何预付款吗?你找到解决办法了吗?

标签: c# winforms event-handling childwindow formclosing


【解决方案1】:

我会提供一个基于接口的解决方案。如果应用程序可以关闭或不可以关闭,这种方式将很容易让您拥有统一的管理方式。通过以下实现,父窗体负责询问子窗口是否已准备好关闭,子窗体执行任何必须执行的操作并回复主窗口。

假设我有接口IManagedForm:

interface IManagedForm
{
    bool CanIBeClosed(Object someParams);
}

两种形式(Form1ChildForm)都可以实现它。

请注意,对于本示例,我以这种方式实例化 ChildForm

ChildForm cf = new ChildForm() { Owner = this, Name = "ChildForm" };
cf.Show();

这里首先是Form1实现的接口

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    object someArgsInterestingForTheMethod = new object();

    e.Cancel = !((IManagedForm)this).CanIBeClosed(someArgsInterestingForTheMethod);
}

// Ask the ChildForm it is done. If not the user should not leave the application.
public bool CanIBeClosed(object someParams)
{
    bool isOKforClosing = true;

    var cf = this.Controls["ChildForm"] as IManagedForm;

    if (cf != null)
    {
        isOKforClosing = cf.CanIBeClosed(someParams);
        if (!isOKforClosing)
        {
            MessageBox.Show("ChildForm does not allow me to close.", "Form1", MessageBoxButtons.OK);
        }
    }
    return isOKforClosing;
}

最后你的ChildForm接口实现看起来像这样:

private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
    object someArgsInterestingForTheMethod = new object();

    e.Cancel = !((IManagedForm)this).CanIBeClosed(someArgsInterestingForTheMethod);
}

public bool CanIBeClosed(object someParams)
{
    // This flag would control if this window has not pending changes.
    bool meetConditions = ValidateClosingConditions(someParams);
    // If there were pending changes, but the user decided to not discard
    // them an proceed saving, this flag says to the parent that this form
    // is done, therefore is ready to be closed.
    bool iAmReadyToBeClosed = true;

    // There are unsaved changed. Ask the user what to do.
    if (!meetConditions)
    {
        // YES => OK Save pending changes and exit.
        // NO => Do not save pending changes and exit.
        // CANCEL => Cancel closing, just do nothing.
        switch (MessageBox.Show("Save changes before exit?", "MyChildForm", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
        {
            case DialogResult.Yes:
                // Store data and leave...
                iAmReadyToBeClosed = true;
                break;
            case DialogResult.No:
                // Do not store data, just leave...
                iAmReadyToBeClosed = true;
                break;
            case DialogResult.Cancel:
                // Do not leave...
                iAmReadyToBeClosed = false;
                break;
        }
    }
    return iAmReadyToBeClosed;
}

// This is just a dummy method just for testing
public bool ValidateClosingConditions(object someParams)
{
    Random rnd = new Random();
    return ((rnd.Next(10) % 2) == 0);
}

希望它足够清楚。

【讨论】:

  • 谢谢,足够清楚地展示了原理。我应该更清楚的是子表单并不总是打开,所以需要一些额外的管道......
  • 这只是了解总体思路的开始。注意:如果你的ChildForm没有打开,那么var cf = this.Controls["ChildForm"] as IManagedForm;这行后面的条件if (cf != null)就不会是trueChildForm不会被请求关闭权限,主窗体将继续其关闭操作。
【解决方案2】:

嗯,Yes/NO/Cancel 的 desicional 逻辑移到了一些中心类。
并且 ChildParentFormClosing 调用相同的函数。

如果从两个FormClosing 函数调用该函数,自然而然地,为了获得良好的用户体验,您必须以一种仅执行一次事件的方式来管理它。

【讨论】:

  • 这似乎是一个非常明智的选择。
【解决方案3】:

MSDN 将 FormClosing 事件解释为在表单关闭之前调用的事件。继续说,对于 MDI,所有子窗体 FormClosing 事件都会在调用父窗体 FormClosing 事件之前调用。

这意味着,当在主窗体上按下关闭按钮时,它会为所有子窗体引发 FormClosing 事件。所以子窗体应该判断它是否准备好关闭,并相应地设置取消属性。当为主窗体引发事件时,它应该已经设置为取消。

即使在主窗体上引发了关闭事件,主窗体也不应该决定子窗体。

如需进一步阅读,

Explanation on Form.FormClosing event at MSDN

Explaining how the cancel property can be used

【讨论】:

  • 在我的情况下,我没有使用 MDI 窗口。
猜你喜欢
  • 1970-01-01
  • 2012-03-27
  • 2012-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-27
  • 2023-03-31
  • 1970-01-01
相关资源
最近更新 更多