在非模态窗口关闭后,窗口的所有资源被释放,窗口不存在,无法获取窗口的任何信息。
何谓模态窗体?简单的可以理解为窗体对话框,用户必须在完成该窗体上的操作或关闭窗体后才能返回打开此窗体的窗体。本文不对模态窗体的定义、特征、功能做具体讨论,主要把重点放在如何在.net窗体应用程序中有效的使用模态窗体,解决使用模态窗体中碰到的常见问题。
模态窗体的属性设置
在.net中一个System.Windows.Forms.Form类就表示一个窗体,通过visual studio 2005设计器能够直接添加窗体,切换到设计模式,在属性窗口中会显示属于该窗体的属性和事件。参照标准的模态窗体,以visual studio 2005程序的菜单工具->选项打开的那个选项对话框为例,对于设计器初始化的窗体还是需要进行一番设置才能达到专业化。令人高兴的是这些设置都可以在设计器模式中通过属性设置实现,笔者将通过代码来实现相应功能,下面对其进行详细描述。
通过对以上属性的设置,基本实现模态窗体的静态功能。对于是否允许调整窗体的大小可根据实际情况而定。
模态窗体中的按钮
模态窗体中(比如visual studio 2005中的“选项”对话框)一般会有两个基本按钮,一个[确定]按钮用来提交,另一个[取消]按钮用来撤销提交,有时候会增加一个[应用]按钮。不过像 “帮助”菜单中的“关于”窗体可能就只有一个[确定]按钮。
Windows窗体为用户操作友好性提供了比较好的支持。我们可以在Form设计界面的属性设置中找到AcceptButton和CancelButton两个属性,默认值为空即显示(无)。在属性中可以通过选择窗体上的按钮来设置值。属性修改生成的代码如下,先定义两个Button:
private System.Windows.Forms.Button buttonOK; private System.Windows.Forms.Button buttonCancel; |
窗体的“接受”按钮:如果设置了此按钮,则用户每次按“Enter”键都相当于“单击”了该按钮。窗体的“取消”按钮:如果设置了此按钮,则用户每次按“Esc”键都相当于“单击”了该按钮。
this.AcceptButton = this.buttonOK; this.CancelButton = this.buttonCancel; |
可见可以通过快捷键来方便的访问特定按钮,但这个有一些例外,比如窗体焦点刚好在buttonCancel上,当按{Enter}时实际按下的键会是 buttonCancel而不是buttonOK,如果焦点停在第三个按钮上,那{Enter}按下相当于点击了该按钮 。另一个细节是通过鼠标点击按钮和快捷键操作按钮的表现行为不一样,快捷键操作Button不会显示按钮被按下的显示效果,看上去什么都没有发生。
模态窗体的打开与关闭
谈到模态窗体的打开,一般通过Form.ShowDialog ()方法或她的一个重载Form.ShowDialog (IWin32Window)来实现,其中后一个方法将窗体显示为具有指定所有者的模态对话框。如下代码所示,
OptionForm form = new OptionForm(); //form.ShowDialog(); form.ShowDialog(this); |
对于指定所有者方式打开的模态窗体可以在窗体内部获取主窗体的引用,
//在模态窗体内部访问所属窗体 MainForm form = this.Owner as MainForm; |
谈到模态窗体的关闭,先来看一下窗体关闭后的返回值。无论是调用Form.ShowDialog ()方法还是Form.ShowDialog (IWin32Window)方法,都会在窗体关闭时返回System.Windows.Forms.DialogResult枚举值。参考 MSDN,该枚举包含的值如下:
由于某些原因在实际用户操作中比如选项数据无法保存,输入的设置数据有问题,点击[确定]按钮需要阻止窗体的关闭以对输入的设置进行调整。对于一些开发者在技术社区贴的阻止窗体关闭的代码,我认为不是很好的实现。以下用代码来描述该实现,注意其中用到了三个事件:
//注册窗体关闭事件 this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OptionForm_FormClosing); //注册确定按钮事件 this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); //注册取消按钮事件 this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); 三个事件对应的事件处理程序如下, //确定按钮处理程序 private void buttonOK_Click(object sender, EventArgs e) { //假设textBoxPath用来记录目录路径,如果不存在要求用户重新设置。 if (this.textBoxPath.Text.Trim().Length == 0) { MessageBox.Show("输入路径信息不对!"); this.textBoxPath.Focus(); } else { this.DialogResult = DialogResult.OK; } } //取消按钮处理程序 private void buttonCancel_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; } //窗体关闭处理程序,在关闭窗体时发生。 private void OptionForm_FormClosing(object sender, FormClosingEventArgs e) { if (this.DialogResult != DialogResult.Cancel && this.DialogResult != DialogResult.OK) e.Cancel = true; } |
上面的代码都正常,就是事件写多了,对上面代码进行修改,去掉[取消]按钮事件和窗体关闭事件以及相关的事件处理程序。首先需要在窗体构造函数中通过设置按钮的DialogResult属性来实现返回特定的DialogResult。
this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; |
注册确定按钮事件,
//注册确定按钮事件 this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); //确定按钮处理程序 private void buttonOK_Click(object sender, EventArgs e) { if (this.textBoxPath.Text.Trim().Length == 0) { MessageBox.Show("输入的路径信息不对!"); this.textBoxPath.Focus(); //设置文本框焦点 this.DialogResult = DialogResult.None; } } |
另外,通过使用WindowsAPI可以禁用或去掉关闭按钮
首先引用添加using System.Runtime.InteropServices;
然后将下面的代码写入工程里面来禁用关闭按钮
private const int SC_CLOSE = 0xF060;
private const int MF_ENABLED = 0x00000000;
private const int MF_GRAYED = 0x00000001;
private const int MF_DISABLED = 0x00000002;
[DllImport("user32.dll", EntryPoint = "GetSystemMenu")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, int bRevert);
[DllImport("User32.dll")]
public static extern bool EnableMenuItem(IntPtr hMenu, int uIDEnableItem, int uEnable);
再将下面代码写在页面加载的page_load事件中就ok啦
IntPtr hMenu = GetSystemMenu(this.Handle, 0);
EnableMenuItem(hMenu, SC_CLOSE, MF_DISABLED | MF_GRAYED);
去掉关闭按钮
[DllImport("USER32.DLL")]
private static extern int RemoveMenu(int hMenu, int nPosition, int wFlags);
///
/// 返回值,非零表示成功,零表示失败。
///
/// 窗口的句柄
///
private int RemoveXButton(int iHWND)
{
int iSysMenu;
const int MF_BYCOMMAND = 0x400; //0x400-关闭
iSysMenu = GetSystemMenu(this.Handle.ToInt32(), 0);
return RemoveMenu(iSysMenu, 6, MF_BYCOMMAND);
}
.Net Framework提供的模态窗体
.net Framework为我们提供了一些比较常用的对话框,在开发过程中省了不少事,以下对其进行介绍。
MessageBox。显示可包含文本、按钮和符号(通知并指示用户)的消息框。通过MessageBox.Show 静态方法来打开对话框。
public static DialogResult Show ( string text ); |
public static DialogResult Show ( IWin32Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options, string helpFilePath, HelpNavigator navigator, Object param ) ; |
根据不同的参数可以定制对话框的行为。
另外一些对话框提供了特定功能。
上面列举的文件对话框抽象基类FileDialog是从CommonDialog抽象类继承,因此所有从该类继承的对话框都可以通过 CommonDialog.ShowDialog ();或其重载CommonDialog.ShowDialog (IWin32Window);方法以模态方式打开窗体。