C# 模态窗体详细介绍

何谓模态窗体?简单的可以理解为窗体对话框,用户必须在完成该窗体上的操作或关闭窗体后才能返回打开此窗体的窗体。本文不对模态窗体的定义、特征、功能做具体讨论,主要把重点放在如何在.net窗体应用程序中有效的使用模态窗体,解决使用模态窗体中碰到的常见问题。

  模态窗体的属性设置 
  在.net中一个System.Windows.Forms.Form类就表示一个窗体,通过visual studio 2005设计器能够直接添加窗体,切换到设计模式,在属性窗口中会显示属于该窗体的属性和事件。参照标准的模态窗体,以visual studio 2005程序的菜单工具->选项打开的那个选项对话框为例,对于设计器初始化的窗体还是需要进行一番设置才能达到专业化。令人高兴的是这些设置都可以在设计器模式中通过属性设置实现,笔者将通过代码来实现相应功能,下面对其进行详细描述。

  • Form.StartPosition属性,确定窗体第一次出现时的位置。这里设置为在父窗体的中间显示。 this.StartPosition = FormStartPosition.CenterParent;
  • Form.HelpButton属性,确定窗体的标题栏上是否有“帮助”按钮。设置显示,看上去更人性化,但实际不一定会对帮助功能进行实现。 this.HelpButton = true;
  • Form.MaximizeBox属性,确定窗体标题栏的右上角是否有最大化框。设置不让她显示。this.MaximizeBox = false;
  • Form.MinimizeBox属性,确定窗体标题栏的右上角是否有最小化框。设置不让他显示。 this.MinimizeBox = false;
  • Form.ShowIcon属性,指示是否在窗体的标题栏中显示图标。设置不显示。 this.ShowIcon = false;
  • Form.ShowInTaskbar属性,确定窗体是否出现在Windows任务栏中。这个当然要节省任务栏的宝贵空间。this.ShowInTaskbar = false;
  • Form.FormBorderStyle属性,指示窗体的边框和标题栏的外观和行为。设置这个属性将不允许拖动调整窗体的大小。this.FormBorderStyle = FormBorderStyle.FixedSingle;
  • Form.ControlBox属性,确定窗体是否有“控件/系统”菜单框。通过该设置可以隐藏标题栏的控制按钮。在有些时候还是有必要设置为False,标题栏就不会再有控制按钮。 this.ControlBox = false;

  通过对以上属性的设置,基本实现模态窗体的静态功能。对于是否允许调整窗体的大小可根据实际情况而定。

  模态窗体中的按钮 
  模态窗体中(比如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.Owner属性会是空引用。

 

  谈到模态窗体的关闭,先来看一下窗体关闭后的返回值。无论是调用Form.ShowDialog ()方法还是Form.ShowDialog (IWin32Window)方法,都会在窗体关闭时返回System.Windows.Forms.DialogResult枚举值。参考 MSDN,该枚举包含的值如下:

  • DialogResult.Abort,对话框的返回值是 Abort(通常从标签为“中止”的按钮发送)。
  • DialogResult.Cancel,对话框的返回值是 Cancel(通常从标签为“取消”的按钮发送)。
  • DialogResult.Ignore,对话框的返回值是 Ignore(通常从标签为“忽略”的按钮发送)。
  • DialogResult.No,对话框的返回值是 No(通常从标签为“否”的按钮发送)。
  • DialogResult.None,从对话框返回了 Nothing。这表明对话框继续运行。
  • DialogResult.OK,对话框的返回值是 OK(通常从标签为“确定”的按钮发送)。
  • DialogResult.Retry,对话框的返回值是 Retry(通常从标签为“重试”的按钮发送)。
  • DialogResult.Yes,对话框的返回值是 Yes(通常从标签为“是”的按钮发送)。

  由于某些原因在实际用户操作中比如选项数据无法保存,输入的设置数据有问题,点击[确定]按钮需要阻止窗体的关闭以对输入的设置进行调整。对于一些开发者在技术社区贴的阻止窗体关闭的代码,我认为不是很好的实现。以下用代码来描述该实现,注意其中用到了三个事件:

 

//注册窗体关闭事件 
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);

        /// <summary>
        /// 返回值,非零表示成功,零表示失败。
        /// </summary>
        /// <param name="iHWND">窗口的句柄</param>
        /// <returns>是否成功</returns>
        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 ) ;

 

  根据不同的参数可以定制对话框的行为。

  另外一些对话框提供了特定功能。

  • OpenFileDialog。打开文件对话框,从FileDialog类继承,提示用户打开文件,无法继承此类。对于文件的打开操作属于比较常见的。
  • SaveFileDialog。保存文件对话框,从FileDialog类继承,提示用户选择文件的保存位置。无法继承此类。
  • FolderBrowserDialog。目录浏览对话框,从CommonDialog类继承,提示用户选择文件夹。无法继承此类。
  • FontDialog。字体设置对话框,从CommonDialog类继承,提示用户从本地计算机上安装的字体中选择一种字体。可继承该类。
  • ColorDialog。颜色设置对话框,从CommonDialog类继承,表示一个通用对话框,该对话框显示可用的颜色以及允许用户定义自定义颜色的控件。可继承该类。
  • PageSetupDialog。打印页面设置对话框,从CommonDialog类继承,允许用户更改与页面相关的打印设置,包括边距和纸张方向。无法继承此类。
  • PrintDialog。打印对话框,从CommonDialog类继承,允许用户选择一台打印机并选择文档中要打印的部分。无法继承此类。
  • PrintPreviewDialog。打印预览对话框,从Form类继承,表示包含 PrintPreviewControl 的对话框窗体。可继承该类。由于该类从Form类继承,所以除了通过 
    PrintPreviewDialog.ShowDialog (); 
    PrintPreviewDialog.ShowDialog (IWin32Window); 
    方法以模态方式打开窗体外,还可以通过PrintPreviewDialog.Show ();或其重载PrintPreviewDialog.Show (IWin32Window);方法按正常非模态方式打开。

  上面列举的文件对话框抽象基类FileDialog是从CommonDialog抽象类继承,因此所有从该类继承的对话框都可以通过 CommonDialog.ShowDialog ();或其重载CommonDialog.ShowDialog (IWin32Window);方法以模态方式打开窗体。

posted @ 2011-04-21 21:44  逆时针  阅读(12688)  评论(0编辑  收藏  举报