C# WinForm 点击按钮显示唯一窗体

小菜最近遇到了一个问题:就是弄了个按钮,点击按钮弹出一个窗体,但是,每点击一次,生成一个窗体,这样就产生了好多同样的窗体。小菜的预期是:点击按钮显示窗体,再次点击的时候,还是显示那个窗体,如果最小化了,那就还原显示出来;如果点击X号关闭了,那就重新弹出一个新的窗体。
需求:以两个窗体为例:窗体1设为主窗体(main函数里面创建的),在窗体1中设一个按钮,点击按钮弹出窗体2,然后再次点击按钮,依旧是显示那个窗体2,而不是生成一堆的窗体2,并且窗体2显示在最前面;如果最小化了,点击窗体1中的按钮,那个窗体2就会还原,如果选择关闭窗体2,点击按钮就会重新弹出窗体2
首先,基本操作:窗体1中弹出窗体2,核心代码如下:

 private void btnShowForm2_Click(object sender, EventArgs e)
        {
            nextForm2 fm2 = new nextForm2();
            fm2.Show();
        }

这样窗体1中的按钮就能弹出窗体2了,但是

一、问题来了

只要点击按钮,窗体2就会无限制的弹出:
小菜希望是:只显示唯一的窗体2*
分析一下,就是每点击一次按钮,都会创建一个新的窗体2对象,这样就会有很多个窗体2被Show出来。
小菜就想了,试着去只用一个窗体2对象,于是选择将创建窗体2对象的语句拿到外面来。

 nextForm2 fm2 = new nextForm2();
        private void btnShowForm2_Click(object sender, EventArgs e)
        {
            //nextForm2 fm2 = new nextForm2();
            fm2.Show();
        }

这样确实点击按钮只显示唯一窗体了,但是

二、又有新问题了

窗体2并没有随着按钮的点击而显示在最前面。当把窗体2对象的创建写在事件外面,而且是再次点击按钮弹出窗体2时,窗体2未显示在最前面,这时小菜想到了焦点问题:
小菜试着设置一下窗体2的焦点。
曾尝试直接在窗体2里面操作:未成功

 private void nextForm2_Load(object sender, EventArgs e)
        {
            //this.Focus();//未成功
        }

        private void nextForm2_Shown(object sender, EventArgs e)
        {
            //this.Focus();//未成功
        }

于是选择在窗体1里面操作

 private void btnShowForm2_Click(object sender, EventArgs e)
        {
            //nextForm2 fm2 = new nextForm2();
            fm2.Show();
            fm2.Focus();
        }

这样完成了窗体2随着按钮的点击显示到了最前面。但是

三、新问题来了

窗体2一旦最小化,就不能再还原了,小菜意识到,即使弄好焦点,过了显示在最前面那一关,还有最小化、最大化等问题在等待解决。
为啥会有这个想法呢,小菜曾见过,某电脑软件,如某管家,点击某某管理按钮,就会进入新窗口,单击最小化之后再点击某某管理按钮依旧会进入那个界面。对于关闭,也是这样,也能再次弹出该页面。而且小菜还发现,窗体2的位置也固定,大小也固定,感觉位置和窗体1一样,感觉在屏幕中间呢。。。。
**接下来是将已经最小化的窗体2再次弹出来,小菜尝试一下哈:
**

 public partial class mainForm : Form
    {
        public mainForm()
        {
            InitializeComponent();
        }
        nextForm2 fm2 = new nextForm2();
        private void btnShowForm2_Click(object sender, EventArgs e)
        {
            //nextForm2 fm2 = new nextForm2();
            fm2.Show();
            fm2.Focus();
            //fm2.Activate();
            //当窗体最大化或最小化之后能够还原
            fm2.WindowState = FormWindowState.Normal;
            //fm2.Focus();
        }
    }

这样,窗体最小化或最大化之后,点击窗体1中的按钮,窗体2能够还原,但是呢,

四、问题依然有

***关闭窗体2之后,点击按钮,报错“无法访问已经释放的对象”***用了系统提供的属性: fm2.WindowState = FormWindowState.Normal; 解决了最大化、最小化问题之后,小菜又遇关闭问题。
在这里插入图片描述
那么,进行到这里,看来需要加个判断了,毕竟窗体2关闭的时候,连带着依托它所创建的对象也被释放掉了。小菜尝试一下哈:

nextForm2 fm2 = new nextForm2();
        private void btnShowForm2_Click(object sender, EventArgs e)
        {
            if (fm2 !=null)
            {
                fm2.Show();
                fm2.Focus();
                fm2.WindowState = FormWindowState.Normal;
            }
        }

但是,还是那个问题:

无法访问已释放的对象
在这里插入图片描述
在这里插入图片描述
此刻,网络搜索求助,发现与C#垃圾回收机制有关,特别感谢那篇文章:
C# WinForm:无法访问已释放的对象
采用它判断窗体对象是否释放的方式当然可以,即:

 nextForm2 fm2 = new nextForm2();
        private void btnShowForm2_Click(object sender, EventArgs e)
        {
            if (fm2 != null)
            {
                if (fm2.IsDisposed)
                {
                    fm2 = new nextForm2();
                    fm2.Show();
                    fm2.Focus();
                    fm2.WindowState = FormWindowState.Normal;
                }
                else//如果窗体2没有被释放掉
                {
                    fm2.Show();
                    fm2.Focus();
                    fm2.WindowState = FormWindowState.Normal;
                }                
            }
        }

到此,问题基本解决。
其实,对于问题三、问题四,小菜想到了另一种办法:
利用静态资源共享这一特性,新建静态类,在它里面声明一个窗体2类型的静态字段,让它来保存依托于窗体2而创建的对象

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace _03_点击按钮显示唯一窗体2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //Form2 fm2 = new Form2();
        private void btnShowForm2_Click(object sender, EventArgs e)
        {
            //判断窗体2对象是否存在,存在则删除旧对象
            if (Test.fm2 != null)
            {
                Test.fm2.Close();
            }
            //这样每次点击按钮,窗体2对象都被刷新过
            Form2 fm2 = new Form2();
            fm2.Show();

        }
    }
}

amespace _03_点击按钮显示唯一窗体2
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 当窗体2加载的时候,保存窗体2对象
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form2_Load(object sender, EventArgs e)
        {
            Test.fm2 = this;
        }

    }


    public static class Test
    {
        public static Form2 fm2;
    }

}

这样做的话,就是在按钮产生效果(窗体2弹出)之前,如果窗体2 对象存在就关闭,然后弄一个新的窗体2对象,就实现了点击按钮显示唯一窗体。
还有,窗体1和窗体2的位置也想设置一下,直接在窗体属性面板中设置,或者在合适的位置写控制窗体位置的代码
在这里插入图片描述
还有一点,在设置窗体第一次出现时的位置的时候,小菜曾经尝试直接用代码操控,但是未起作用。回顾自己的操作,Load、Shown事件里面都用过,然而看从窗体属性面板设置好之后的Form1.Designer.cs里面的代码,看到控制窗体位置的代码是在Load和Show之前的,那小菜萌生一种念头,再试一次,这次自己写在Load和Show的前面。结果可行:窗体1与窗体2都显示在了屏幕中间。

  public Form1()
        {
            InitializeComponent();
            //设置窗体1出现在屏幕中间,这个写在Load、Shown里面皆不起作用
            this.StartPosition = FormStartPosition.CenterScreen;
        }
private void btnShowForm2_Click(object sender, EventArgs e)
        {
            //判断窗体2对象是否存在,存在则删除旧对象
            if (Test.fm2 != null)
            {
                Test.fm2.Close();
            }
            //这样每次点击按钮,窗体2对象都被刷新过
            Form2 fm2 = new Form2();
            fm2.StartPosition = FormStartPosition.CenterScreen;
            fm2.Show();
            //当前只能在窗体属性面板中设置了--不,要写在Show之前。
            //fm2.StartPosition = FormStartPosition.CenterScreen;

        }

至此,小菜的任务完成

时隔几天后,小菜忽然想:试试模式对话框怎么样,但是结果显然是不理想的:

/// <summary>
        /// 测试,采用模式对话框弹出窗体也能显示唯一窗体
        /// 虽然满足显示唯一、满足关闭后能打开(当然打开的新new的,不再是原来的
        /// 但从视觉上看没啥变化)
        /// 但不能满足小菜的预期:
        /// 最小化连带着主窗体一起最小化;最大化之后想去主窗体,必须先关闭了
        /// 子窗体才能操作主窗体(这里就深深地感受到了模式对话框的局限性)。
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnShowForm3_Click(object sender, EventArgs e)
        {
            Form fm3 = new Form();
            fm3.ShowDialog();
        }

这正如小菜在注释里面写的那样:尽管使用模式对话框弹出窗体也实现显示唯一窗体,但这里是不合要求的:
虽然满足显示唯一、满足关闭后能打开(当然打开的新new的,不再是原来的,但从视觉上看没啥变化) 等需求,但是–不能满足小菜的预期:

  1. 子窗体最小化连带着主窗体一起最小化;其最大化之后想去主窗体操作,必须先关闭了这个模式子窗体

  2. 只有子窗体关闭了,才能操作主窗体(这里就深深地感受到了模式对话框的局限性)。

再会,小菜。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值