C# 如何关闭多窗体应用程序中的特定窗体

4
我希望你能帮我翻译这段内容。我的应用程序正在监视数据库中的警报。当数据库中出现警报时,我的应用程序将其添加到主窗体中的datagridview中,并根据其优先级创建一个小的winform弹出窗口来显示事件。
在datagridview中有一个按钮可以将警报标记为“已查看”,然后它将更新数据库并从列表中消失。但是该事件的弹出窗口仍然打开。
有人知道如何关闭此窗口吗?我需要找到可能打开的多个警报窗口中的特定窗口。
迄今为止,我最接近的代码如下:
private void closeForm(int id)
{
    foreach (Form f in Application.OpenForms)
    {
        if (Convert.ToString(id) == f.Name)
        {
            this.Close();
        }
    }
}

这个方法一直有效,直到关闭正确的表单时出现错误。然后会显示一个错误信息,指出“集合已修改;可能无法执行枚举操作。” 这有点有道理,但我简单地想不出另一种方法。

我有一个名为Alert的winform类,它创建新的表单。正如您所看到的,它们将获得一个标准文本“警报”和基于警报ID的唯一名称。

Alert alertform = new Alert(id);
alertform.Name = formid;
alertform.Text = "Alarm";
alertform.Show();

我希望有人能给我一些好的想法,我已经搜索过,但实在找不到一个简单而优雅的方法来完成这个任务。

6个回答

4
您只需要在DataGridView或其DataSource中存储对表单的引用,然后使用该引用关闭表单即可。此方法比迭代所有应用程序表单更不容易出现问题。
最好的方法可能是向DataGridView添加一个隐藏列来保存表单ID,然后还有一个Dictionary<int, Form>,您可以使用它来获取要关闭的表单的引用。
然后,您只需从字典中获取表单引用并调用其关闭方法即可:
private void CloseAlertForm(int id)
{        
    Form f = dict[id];
    f.Close();
    dict.Remove(id);
}

此外,您可以存储Action委托,而不是表单引用,这样您就可以稍微解耦警报表单和网格表单。

你能否为此输入一小段示例代码?不是为了存储DataGridView的引用,而是如何使用它来关闭窗体。 - Mike van Heugten
你们所有的人(也许还有女生),我惊讶于我得到了多少好答案,而且速度非常快。我找到了一个适合我的解决方案,不是说其他解决方案不好。但是,我只需要一个 :-) 而这个对我来说最容易实现。感谢大家的回应!Mike - Mike van Heugten

4

在关闭表单之后,您需要在循环中添加break;。当关闭表单时,集合将被修改(该表单将从集合中删除),从而使foreach循环无效。您应该调用f.Close而不是this.Close吗?

private void closeForm(int id)

    {
        foreach (Form f in Application.OpenForms)

            if (Convert.ToString(id) == f.Name)
            {
                f.Close();
                break;

            }
    }

这看起来是我问题的一个非常好和简单的解决方案。但出于某种原因,它会关闭我的整个应用程序。 - Mike van Heugten
@Mike 你把 "this.Close();" 改成了 "f.Close();" 吗? - Miika L.
@Mike 在编辑时添加了该评论,因为我在发布代码示例后才注意到它,很抱歉。但很高兴你找到了解决方案。 - Miika L.
这只能同时显示一个警告,但它会工作 :D - Julien Roncaglia
据我理解,他只想关闭被点击为“已查看”的警报,而不是所有警报。 - Miika L.
它的工作非常流畅,即使打开了11个警报表单,它也能正确地关闭只有那一个。这正是我需要它做的事情。 - Mike van Heugten

3
只需从 foreach 循环中获取引用,然后在其外部关闭 form 即可。
private void closeForm(int id)
{   
        Form formtoclose=null;
        foreach (Form f in Application.OpenForms)
        {
            if (Convert.ToString(id) == f.Name)
            {
                formtoclose = f;
            }
        }
        if(formtoclose!=null)
        formtoclose.Close();
}

或者使用LINQ ;)var f = Application.OpenForms.Cast
().Where(form => form.Name == id).FirstOrDefault(); if (f != null) f.Close(); - Konrad Morawski
你的代码无法编译,因为 formtoclose 没有被赋值。在循环之前,你需要将 formtoclose 设置为 null(并且显然在调用 Close 之前检查它是否仍然为 null)。 - Konrad Morawski
是的,我检查过了,它无法编译。 - Mike van Heugten
+1,这是对问题背后的两个问题最直接的修复:关闭此表单而不是找到表单,并在ForEach中执行它。 - TheBlastOne
@Konrad Application.Forms是一个“原始”的列表;它不支持像'Cast这样的Linq操作符。这段代码永远不会编译。 - BillW

-1

Close 方法会修改 OpenForms 集合,因此您可以枚举副本而不是直接枚举 OpenForms 集合。

LINQ 在创建副本方面非常方便,例如:

foreach (Form f in Application.OpenForms.Where(i => Convert.ToString(id) == i.Name).ToList()) {
      // Save to close the form here
}

ToList执行查询并存储副本。


Application.Forms是一个“原始”的列表;它不支持像'Where这样的Linq运算符。这段代码永远不会编译。 - BillW

-2
你可以使用表单的类型来查找它们(并使用ToArray创建一个新集合,避免更改正在枚举的集合)。
private void CloseAlerts()
{
    var openForms = Application.OpenForms.Cast<Form>();
    foreach (var f in openForms.Where(f => f is Alert).ToArray())
    {
        f.Close();
    }
}

在这种情况下,您不需要设置名称:

Alert alertform = new Alert(id);

alertform.Text = "Alarm";

alertform.Show();

大家好(也许还有女生),我对得到这么多出色的答案感到惊讶,而且速度也很快。我找到了一个对我有效的解决方案,不是说其他的解决方案不好。但是嘛,我只需要一个 :-) 而且这个对我来说实施起来最简单。非常感谢大家的回应!Mike - Mike van Heugten
Application.Forms是一个“原始”的列表;它不支持像'Where这样的Linq运算符。这段代码永远不会编译。 - BillW

-2
var names = Application.OpenForms.Select(rs=>rs.name).ToList()

foreach (string name in names)
    if (Convert.ToString(id) == name)
    {
        Application.OpenForms[name].Close();
    }

Application.Forms是一个“原始”的列表;它不支持像“Select”这样的Linq运算符。这段代码永远不会编译。 - BillW

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