【问题标题】:Close a Form or multiple Forms opened in a UserControl关闭在用户控件中打开的一个或多个窗体
【发布时间】:2020-05-13 16:09:48
【问题描述】:

我有一个主窗体 (Form1) 和一个用户控件 (ucModule2)。
ucModule2 包含一个按钮 (simpleButton1),它打开另一个窗体 (Form2)。
Form1 上,我有一个按钮(UsrCtrlDialog),它应该关闭Form2

我有两种情况:

场景 1: 我的ucModule2.cs

public partial class ucModule2 : UserControl
{
    public static Form2 fr2 = new Form2();
    private void simpleButton1_Click(object sender, EventArgs e)
    {
        //Form2 fr2 = new Form2();
        fr2.Show();
        fr2.TopMost = true;
        textModule2 = textBox_ucModule2.Text;
    }
 }

Form1的按钮用来关闭Form2

private void UsrCtrlDialog_Click(object sender, EventArgs e)
{
    // Form2 fr2 = new Form2();            
    ucModule2.fr2.TopMost = false;
    ucModule2.fr2.Close();
    ucModule2.fr2.Dispose();
}

在这种情况下,Form2 仅打开一次,并在 Form1 中的 UsrCtrlDialog 按钮关闭时关闭。但是如果我想再次打开Form2,我会得到fr2.Show()

System.ObjectDisposedException exception ('无法访问已处置的 对象。'`

我知道当我想再次打开Form2 时,不会创建Form2 类型的新对象。
关闭旧的Form2后,我该怎么做才能打开新的Form2?

场景 2: 我的ucModule2.cs

public partial class ucModule2 : UserControl
{
    //public static Form2 fr2 = new Form2();
    private void simpleButton1_Click(object sender, EventArgs e)
    {
        Form2 fr2 = new Form2();
        fr2.Show();
        fr2.TopMost = true;
        textModule2 = textBox_ucModule2.Text;
    }
}

在这种情况下,我可以打开与单击simpleButton1 一样多的Form2 窗口。比如我按了3次simpleButton1。我将有 3 个Form2 窗口。

当我按UsrCtrlDialog 按钮表单Form1 时,如何关闭3 个Form2 窗口?如何将fr2 对象从ucModule2.cs 获取到Form1.cs

【问题讨论】:

  • 如果我把 ucModule2.FormInstance.Close();在 UsrCtrlDialog_Click 中,我收到此错误:“非静态字段、方法或属性 'ucModule2.FormInstance' 需要对象引用”。我将用我的代码为我的问题添加答案。
  • 您没有创建 UserControl 的实例。您正在使用类型。您只能使用类型查看静态属性/字段。但是你必须使用 UC 的一个实例,当然,不是它的 Type。

标签: c# forms winforms user-controls


【解决方案1】:

在您的代码中,您将 UserControl 视为静态类对象,使用它的类型来访问字段和属性:

public partial class ucModule2 : UserControl 
{ 
     public static Form2 fr2 = new Form2();
     // (...)
}

那么,在Form1

ucModule2.fr2.TopMost = false;
// (...)

ucModule2 与 UserControl 类型的名称相同,因此您尝试使用 Type 来设置属于 的字段/属性该控件的实例

如果您将ucModule2 的实例添加到表单,则设计器会将UC 的第一个实例重命名为ucModule21
像往常一样,将索引值(1,如果它是该类型的第一个实例)添加到创建的类型的名称。

您需要使用该 UserControl(或任何其他控件)的 Instance 成员,而不是其 Type。

有关该主题的一些文档:

Inheritance (C# Programming Guide)
Members (C# Programming Guide)

类和结构都有代表它们的数据的成员和 行为。一个类的成员包括所有在类中声明的成员 类,以及所有成员(构造函数和终结器除外) 在其继承层次结构中的所有类中声明...

Static Classes and Static Class Members

创建一个只允许一个自身实例的非静态类 要创建,请参阅:
Implementing Singleton in C#

在为类型分配名称时遵循标准命名约定也很重要。大多数开发人员假设类型名称使用 Pascal Case 约定,而此类型的实例将使用 Camel Case 约定命名,如下所示:

MyUserControl myUsrControl = new MyUserControl();
myUsrControl.Show();

您还可以在此处查看两者使用的不同标记颜色


在 Form1 中

Form1(其父窗体)调用UserControl 的SetForm() 方法。
之后,Form1 可以使用 UserControl 的 public FormInstance 属性。

public partial class Form1: Form
{
    // If an Instance of the UC has been added in the Form's Designer,
    // use that instance reference instead 
    UCModule2 ucModule2 = new UCModule2();

    private void Form1_Load(object sender, EventArgs e)
    {
        ucModule2.Location = new Point(100, 100);
        this.Controls.Add(ucModule2);
        ucModule2.SetForm(typeof(Form2));
    }

    private void UsrCtrlDialog_Click(object sender, EventArgs e)
    {
        ucModule2?.FormInstance?.Close();
    }
}

在 UCModule2 中(使用正确的大小写重命名类型):

如果表单实例已被父表单关闭/处置,请重新创建一个新实例并重置公共 FormInstance 属性。
可以判断Form的实例是否被销毁,测试:

FormInstance is null || FormInstance.IsDisposed

public partial class UCModule2: UserControl
{
    public Form FormInstance { get; private set; }

    public Form SetForm(Type formType)
    {
        if (this.FormInstance == null || this.FormInstance.IsDisposed) {
            this.FormInstance = (Form)Activator.CreateInstance(formType);
        }
        return this.FormInstance;
    }

    private void simpleButton1_Click(object sender, EventArgs e)
    {
        if (this.FormInstance is null || this.FormInstance.IsDisposed) {
            this.SetForm(FormInstance.GetType());
        }
        this.FormInstance?.Show();
    }
}

处理运行时生成的不同表单类型的集合

如果在此 UserControl 处于活动状态时需要生成多个表单,我们可以将由 UC 的父表单确定的表单类型的每个新实例添加到列表中。然后在父窗体决定 和/或当 UserControl 本身被销毁时处理列表中的每个窗体实例:

父Form可以调用SetForm(Type formType)公共方法,设置生成的Form类型。然后调用 CloseAllForms() 公共方法在需要时将它们全部关闭。 UC 在其句柄被销毁时调用相同的方法,以删除现有的 Form 实例(如果需要)。

表单更改表单类型只需调用SetForm() 与另一种类型:

ucModule2.SetForm(typeof(Form2));
// (... and after...)
ucModule2.SetForm(typeof(Form3));

UC 的按钮将生成指定的新表单类型。

In Form1

public partial class Form1: Form
{
    // If an Instance of the UC has been added in the Form's Designer,
    // use that instance reference instead 
    UCModule2 ucModule2 = new UCModule2();

    private void Form1_Load(object sender, EventArgs e)
    {
        ucModule2.Location = new Point(100, 100);
        this.Controls.Add(ucModule2);
        ucModule2.SetForm(typeof(Form2));
    }

    private void UsrCtrlChangeType_Click(object sender, EventArgs e)
    {
        ucModule2.SetForm(typeof(Form3));
    }

    private void UsrCtrlDialog_Click(object sender, EventArgs e)
    {
        ucModule2.CloseAllForms();
    }
}

In UCModule2

public partial class UCModule2: UserControl
{
    List<Form> formsCollection = null;

    public UCModule2()
    {
        InitializeComponent();
        formsCollection = new List<Form>();
    }

    private Type FormType { get; set; }

    // Check whether the new type is different before setting the property,
    // in case the FormType property has an explicit setter.
    public void SetForm(Type formType)
    {
        if (this.FormType != formType) {
            this.FormType = formType;
        }
    }

    public void CloseAllForms()
    {
        if (formsCollection != null && formsCollection.Count > 0) {
            for (int i = formsCollection.Count - 1; i >= 0 ; i--) {
                formsCollection[i].Dispose();
            }
        }
    }

    protected override void OnHandleDestroyed(EventArgs e)
    {
        CloseAllForms();
        base.OnHandleDestroyed(e);
    }

    private void btnShowForm_Click(object sender, EventArgs e)
    {
        if (FormType == null) return;
        var instance = (Form)Activator.CreateInstance(FormType);
        formsCollection.Add(instance);
        instance.Show();
    }

【讨论】:

  • 感谢您更新您的答案!我已经测试过了,但是当我点击 btnShowForm 按钮时没有打开任何表单。
  • btnShowForm_Click 当然是你的simpleButton1_Click 处理程序。我忘了重命名它,因为那是我用于测试的 Button 的名称。改个名字就好了。
  • 我想通了。我已经看到它打开了与单击 simpleButton1 一样多的 Form3 窗口。但它发生在我单击 UsrCtrlChangeType 按钮后。现在我想弄清楚如何从 ucModule2 中打开不同的按钮,Form2 和 Form3。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多