如何使用MVP处理FormClosing事件

3

这里有一个长成这个样子的视图:

public interface IAddressView
{
     void Close();
     bool IsDirty();
     bool DoesUserWantToDiscardChanges();
}

我有一个类似这样的演示者:

public class AddressViewPresenter
{
    private IAddressView m_addressView;
    private Address m_address;

    public AddressViewPresenter(IAddressView addressView)
    {
        m_addressView = addressView;
        m_address = new Address();
    }

    public void CancelButtonClicked()
    {
        bool canClose = true;

        if (m_addressView.IsDirty())
        {
            canClose = m_addressView.DoesUserWantToDiscardChanges();
        }

        if (canClose)
        {
            m_addressView.Close();
        }
    }

    public void SaveButtonClicked()
    {
        // saving logic goes here...removed for brevity
        m_addressView.Close();
    }
}

我随后有一个Windows表单,其中包含取消按钮、保存按钮和显示地址的所有控件。 取消按钮运行:

m_addressPresenter.CancelButtonClicked();

接下来检查视图是否已更改,并提示用户放弃任何更改。这很好,这正是我想要的。

现在我的问题是,如果用户关闭表单而没有点击取消(即他们点击右上角的“X”甚至按下ALT+F4),如何实现相同的效果。我尝试处理FormClosing事件,但最终导致了代码重复,并且如果我点击取消按钮,弹出窗口会出现两次。以下是我的代码:

private void AddressForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (this.IsDirty())
    {
        if (!this.DoesUserWantToDiscardChanges())
        {
            e.Cancel = true;
        }
    }
}
3个回答

3

我知道这已经很旧了,但一开始这让我感到困惑。这是我处理这个问题的方式。希望有人偶然发现并发现这很有用。

在视图的界面中。

public interface IMainScreenView
{
    event BoolHandler CloseView;

    bool IsDirty { get; set; }
    bool WillDiscardChanges();
}

然后在您的Presenter中,将关闭函数订阅到CloseView事件。
public MainScreenPresenter(IView _view)
{
    _view.CloseView += Close;
}

private bool Close()
{
    bool close = true;

    if (_view.IsDirty)
        close = _view.WillDiscardChanges();

    return close;
}     

现在,在视图本身中,您可以像往常一样订阅FormClosing事件并使用CloseView事件。

public MainForm()
{
    InitializeComponent();

    _presenter = new MainScreenPresenter(this);
    FormClosing += UxMainScreenFormClosing;
}

public bool WillDiscardChanges()
{
    return MessageBox.Show("Changes have been made without being saved. Would you like to continue?", "Exiting", MessageBoxButtons.YesNo) == DialogResult.Yes;
}

protected void UxMainScreenFormClosing(object sender, FormClosingEventArgs e)
{
    e.Cancel = !CloseView();
}

为了完整起见,我还包括了“放弃更改”的检查。这样可以保持视图的清洁,并将逻辑隐藏在Presenter中。希望这有所帮助。 :)


我知道这个回复是很多年之后的事情了,但在世界上有BoolHandler这个事件吗?我找不到任何相关的参考。如果你还在关注这个帖子,能否详细说明一下?谢谢。 - Moshe Yalovsky
@MosheYalovsky老实说,我自己都记不清了。我想我可能只是创建了一个名为BoolHandler的自定义事件处理程序,并错误地包含了它。不过,这个想法是在视图中有一个事件处理程序,而主持人订阅它。希望这能帮到你。 - yellow_nimbus

0

如果有人遇到这个问题并且想要一个简单的解决方案。

如果您的Presenter创建了一个视图实例然后打开它,请将以下内容放在您的Presenter构造函数中:

form1.FormClosed += OnFormClose; //form1 is the name of the form that the presenter opens

以下是一个处理程序:

private void OnFormClose(object sender, EventArgs e)
        {
            Application.Exit();
        }

0
你面临的主要问题是视图中包含了应该由主持人(presenter)负责的逻辑,所以我建议将主持人的 CancelButtonClicked 方法改为类似以下的形式:
public bool ViewRequestingClose()
{
    bool canClose = true;
    if (m_addressView.IsDirty())
    {
        canClose = m_addressView.DoesUserWantToDiscardChanges();
    }
    return canClose;
}

返回值表示视图是否应继续关闭。

视图中的两个事件处理程序用于取消按钮点击和表单关闭事件,然后查询Presenter以确定它们是否应该继续关闭,例如。

private void AddressForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if(!m_addressPresenter.ViewRequestingClose())
        e.Cancel = true;
}

private void CancelButton_Click(object sender, FormClosingEventArgs e)
{
    if(m_addressPresenter.ViewRequestingClose())
        this.Close();
}

这样做确实更有道理。然而,我仍然遇到一个问题,在单击取消按钮时会两次询问是否要放弃更改。 - smoak
你之所以会得到两次提示,是因为当点击取消按钮确认关闭表单时,它会触发与直接尝试关闭表单时相同的逻辑。你可以使用一个布尔变量来跟踪是通过快捷键Alt+F4/"x"关闭还是通过取消按钮关闭,并且只在第一次关闭时提示。 - Lee Roth
是的。我只是希望能以不同的方式完成它。如果我添加一个布尔值来跟踪关闭是否来自取消按钮,那么它就按照你描述的方式工作。我想这种方式有点笨拙。不过,如果它能正常工作,那就好了。谢谢。 - smoak
为什么不只是在CancelButton_Click事件中使用this.Close();?它会产生与使用布尔值指示关闭来自CancelButton_Click相同的效果。 - Firo

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