摘要:本文闡述了在基於.NET平台的Windows程式開發中使用模態表單的諸多方面,部分內容延伸到一般表單的應用。
概述
何謂模態表單?簡單的可以理解為表單對話方塊,使用者必須在完成該表單上的操作或關閉表單後才能返回開啟此表單的表單。本文不對模態表單的定義、特徵、功能做具體討論,主要把重點放在如何在.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屬性會是Null 參考。
談到模態表單的關閉,先來看一下表單關閉後的傳回值。無論是調用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);方法以模態方式開啟表單。