第一天 -- 《2014-07-15 三层架构》2 -- 登录窗体的一般做法、多文档窗体、窗体控件的基本用法。
一、下午《04、使用SHowDIalog方式关闭登录窗体》
1、登录窗体的一般做法
<1>Main()入口方法中显示登录窗体(ShowDialog方式打开),如果登录OK,再在主窗体上做消息循环。
//入口Main方法中的主要代码: [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); FrmLogin frm = new FrmLogin(); //ShowDialog()方式打开 的窗体会返回一个DialogResult值 DialogResult res = frm.ShowDialog(); if (res == DialogResult.OK) { Application.Run(new FrmMain()); } }
<2>登录窗体主要代码
详见《第一天 -- 《2014-07-15 三层架构》1 -- 三层架构基本概念、一个登陆小例子》第二节第2段重构UI
简单说就是如果登录成功,登录窗体的DialogResult属性赋值OK;取消登录(以及点击右上角x关闭登录窗体)赋值Cancel。
2、“模态对话框”的注意事项
用ShowDialog()显示的窗体,为其DialogResult赋值(除DialogResult.None以外的任何值)后,窗体就会自动关闭。
例子代码如下:
1 //主窗体点击button1,将Form2子窗体以模式对话框打开。 2 private void button1_Click(object sender, EventArgs e) 3 { 4 Form f = new Form2(); 5 f.ShowDialog(); 6 7 //当对话框关闭后,才能接着运行下面代码。 8 DialogResult r = f.DialogResult; 9 MessageBox.Show("Form2对话框选择了:" + r); 10 } 11 12 13 14 //Form2窗体中主要代码: 15 //创建Form2对象后,其DialogResult属性默认为None 16 private void button1_Click(object sender, EventArgs e) 17 { 18 this.DialogResult = DialogResult.OK;//赋值后会自动关闭Form2 19 } 20 21 private void button2_Click(object sender, EventArgs e) 22 { 23 this.Close();//关闭Form2时,DialogResult属性自动设置为Cancel 24 } 25 //点击Form2右上角x关闭窗体时,DialogResult属性也是自动设置为Cancel
3、窗体的关闭Close与清理Dispose
<1>当主窗体关闭时,由它创建并Show的所有窗体(子窗体)都会被关闭。
<2>执行Show()方法打开的窗体被Close掉后,它(占用的资源)会被Dispose清理。但窗体对象引用并不是null,如果此时再去执行其Show()或ShowDialog(),会抛出异常:“System.ObjectDisposedException:“无法访问已释放的对象。”。
<3>而执行ShowDialog()方法打开的窗体被Close掉后,它(占用的资源)不会被清理,窗体对象引用也不是null,如果此时再去执行其Show()或ShowDialog(),不会抛出异常。
<4>总结:当我们使用ShowDialog来显示窗体的时候,当你关闭的时候(不准备再打开),最好手动Dispose一下。为什么是最好呢,因为其实在GC回收垃圾的时候还是会调用窗体的Dispose的,因为在Form的基类的析构函数里面有调用Dispose(false);
~Component()
{
this.Dispose(false);
}
其实在MSDN上微软就对这有说明,顺便吐槽一下中文MSDN的翻译,实在是太烂了。
在以下两种情况下调用 Close 不会清理Dispose窗体(占用的资源):
(1) 窗体是多文档界面 (MDI) 应用程序的一部分且是不可见的;
(2) 您是使用 ShowDialog 显示的该窗体。
在这些情况下,需要手动调用Dispose()来清理窗体(占用的资源)。
另外:第一种情况我测试了下,没有复现出来,MDI里面的子窗口调用Close的时候跟正常一样,每次都会自动Dispose。试了很久还是一样的结果,求高人指定吧。
二、下午《05、设置MDI父子窗体》
1、将主窗体设置为MDI容器,子窗体的MdiParent设置为主窗体
1 ... 2 this.IsMdiContainer = true;//设置为多文档窗体 3 ... 4 5 private void tsmiStudent_Click(object sender, EventArgs e) 6 {//点击按钮,打开子窗体 7 FrmPerson frm = FrmPerson.CreateSingle();//获取单例的子窗体 8 9 frm.MdiParent = this;//指定当前窗体的MDI父容器是当前窗体 10 frm.Show(); 11 }
2、菜单栏添加窗体列表项
在菜单栏加一个菜单项(Windows),即子窗体列表。如下图所示:

做法:将菜单栏MenuStrip的MdiWindowListItem属性指定为某个菜单项即可。
三、下午《06、单例模式》--《07、另外一种方式实现窗体只能打开一次》
1、单例窗体主要代码如下:(当然单例的方式还有几种,这里不介绍了。事实上最好使用微软推荐的方式。即public static readonly 类名 Instance ......)
1 //单例窗体FrmPerson类中 2 ... 3 private static FrmPerson frm = null;//单例对象 4 5 public static FrmPerson CreateSingle() 6 { 7 //IsDisposed标记窗体已经被释放,如果窗体被释放那么必须重新实例化。 8 if (frm == null || frm.IsDisposed) 9 { 10 frm = new FrmPerson(); 11 } 12 return frm; 13 } 14 ...
2、可以用Application.OpenForms["窗体名"]找到(没有的话就实例化)和打开指定窗体。
具体代码如下:
1 ... 2 //主窗体中点击“tsmiClass”按钮,打开FrmClasses子窗体。 3 private void tsmiClass_Click(object sender, EventArgs e) 4 { 5 FrmClasses frm = null; 6 if (Application.OpenForms["FrmClasses"] == null) 7 { 8 frm = new FrmClasses(); 9 frm.MdiParent = this; 10 frm.Show(); 11 } 12 else 13 { 14 Application.OpenForms["FrmClasses"].Show(); 15 } 16 } 17 ...

浙公网安备 33010602011771号