如何检测父窗体取消FormClosing事件?

3

我在主表单中打开了一个子表单,如下所示:

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

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

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

当用户尝试关闭包含未保存数据的主窗体时,主窗体的FormClosing事件处理程序会显示警告“Data not saved. Cancel close?”,他们可以取消保存(即在主窗体的FormClosing事件处理程序中通过FormClosingEventArgs对象设置Cancel标志)。

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

从子窗体中是否可能检测到主窗体取消了FormClosing事件? 我仍然希望在向用户询问在主窗体中保存数据时停止重绘线程。


有进展了吗?你找到解决方案了吗? - Luis Quijada
3个回答

2
我将提供一个基于接口的解决方案。这样做可以让您轻松地统一管理应用程序是否可以关闭。使用以下实现,父窗体负责询问子窗口是否准备好关闭,子窗口执行必要的操作并回复主窗口。
假设我有接口 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);
}

希望您能理解得足够清楚。

谢谢,足以清晰地演示原理。我应该更清楚地表明子窗体并不总是打开的,因此需要一些额外的工作... - g t
这只是为了得到大致的概念而已。注意:如果您的“ChildForm”没有打开,那么此行代码“var cf = this.Controls["ChildForm"] as IManagedForm;”后面的条件“if (cf!= null)”将不成立,子窗体就不会被询问是否允许关闭,主窗体将继续其关闭操作。 - Luis Quijada

1

好的,无论是Yes/NO/Cancel的决策逻辑都要移动到某个中央类中。
而且ChildParentFormClosing都调用同一个函数。

为了良好的用户体验,您需要以一种方式管理它,即使从两个FormClosing函数中调用该函数,也只执行一次。


这似乎是一个非常明智的选择。 - Enigmativity

0

MSDN解释了FormClosing事件,该事件在窗体关闭之前被调用。它继续说,在MDI应用中,所有子窗体的FormClosing事件都会在父窗体的FormClosing事件之前被调用。

这意味着,当主窗体上的关闭按钮被按下时,它会为所有子窗体引发一个FormClosing事件。因此,子窗体应确定自己是否准备好关闭,并相应地设置cancel属性。当主窗体的事件被引发时,它应该已经被设置为cancel。

主窗体不应该代替子窗体做决定,即使关闭事件是在主窗体上引发的。

进一步阅读:

MSDN上关于Form.FormClosing事件的解释

解释如何使用cancel属性


在我的情况下,我并没有使用 MDI 窗口。 - g t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接