熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

談基於.net平台開發中的模式窗體

2022-06-13   來源: .NET編程 

  作者鄭佐
適用於 Windows 操作系統
NET Framework x運行時環境
NET Windows開發Visual Studio

摘要本文闡述了在基於NET平台的Windows程序開發中使用模式窗體的諸多方面部分內容延伸到一般窗體的應用
單擊此處下載本文的示例代碼

內容索引
概述
模式窗體的屬性設置
模式窗體中的按鈕
模式窗體的打開與關閉
窗體的參數傳遞
Net Framework提供的模式窗體
更多資源

概述
何謂模式窗體?簡單的可以理解為窗體對話框用戶必須在完成該窗體上的操作或關閉窗體後才能返回打開此窗體的窗體本文不對模式窗體的定義特征功能做具體討論主要把重點放在如何窗體應用程序中有效的使用模式窗體解決使用模式窗體中碰到的常見問題

模式窗體的屬性設置
中一個SystemWindowsFormsForm類就表示一個窗體通過visual studio 設計器能夠直接添加窗體切換到設計模式在屬性窗口中會顯示屬於該窗體的屬性和事件參照標准的模式窗體以visual studio 程序的菜單工具>選項打開的那個選項對話框為例對於設計器初始化的窗體還是需要進行一番設置才能達到專業化令人高興的是這些設置都可以在設計器模式中通過屬性設置實現筆者將通過代碼來實現相應功能下面對其進行詳細描述
FormStartPosition屬性確定窗體第一次出現時的位置這裡設置為在父窗體的中間顯示
thisStartPosition = FormStartPositionCenterParent;
FormHelpButton屬性確定窗體的標題欄上是否有幫助按鈕設置顯示看上去更人性化但實際不一定會對幫助功能進行實現
thisHelpButton = true;
FormMaximizeBox屬性確定窗體標題欄的右上角是否有最大化框設置不讓她顯示
thisMaximizeBox = false;
FormMinimizeBox屬性確定窗體標題欄的右上角是否有最小化框設置不讓他顯示
thisMinimizeBox = false;
FormShowIcon屬性指示是否在窗體的標題欄中顯示圖標設置不顯示
thisShowIcon = false;
FormShowInTaskbar屬性確定窗體是否出現在Windows任務欄中這個當然要節省任務欄的寶貴空間
thisShowInTaskbar = false;
FormFormBorderStyle屬性指示窗體的邊框和標題欄的外觀和行為設置這個屬性將不允許拖動調整窗體的大小同時Icon屬性將失效並不顯示窗體標題欄圖片
thisFormBorderStyle = FormBorderStyleFixedDialog;
FormControlBox屬性確定窗體是否有控件/系統菜單框通過該設置可以隱藏標題欄的控制按鈕在有些時候還是有必要設置為False標題欄就不會再有控制按鈕
thisControlBox = false;
通過對以上屬性的設置基本實現模式窗體的靜態功能對於是否允許調整窗體的大小可根據實際情況而定
 
模式窗體中的按鈕
模式窗體中(比如visual studio 中的選項對話框)一般會有兩個基本按鈕一個[確定]按鈕用來提交另一個[取消]按鈕用來撤銷提交有時候會增加一個[應用]按鈕不過像幫助菜單中的關於模式窗體可能就只有一個[確定]按鈕Windows窗體為用戶操作友好性提供了比較好的支持我們可以在Form設計界面的屬性設置中找到AcceptButton和CancelButton兩個屬性默認值為空即顯示(無)在屬性中可以通過選擇窗體上的按鈕來設置值屬性修改生成的代碼如下
先定義兩個Button
private SystemWindowsFormsButton buttonOK;
private SystemWindowsFormsButton buttonCancel;
窗體的接受按鈕如果設置了此按鈕則用戶每次按Enter鍵都相當於單擊了該按鈕
thisAcceptButton = thisbuttonOK;
窗體的取消按鈕如果設置了此按鈕則用戶每次按Esc鍵都相當於單擊了該按鈕
thisCancelButton = thisbuttonCancel;
可見可以通過快捷鍵來方便的訪問特定按鈕但這個有一些例外比如窗體焦點剛好在buttonCancel上當按{Enter}時實際按下的鍵會是buttonCancel而不是buttonOK如果焦點停在第三個按鈕上那{Enter}按下相當於點擊了該按鈕另一個細節是通過鼠標點擊按鈕和快捷鍵操作按鈕的表現行為不一樣快捷鍵操作Button不會顯示按鈕被按下的顯示效果看上去什麼都沒有發生

模式窗體的打開與關閉
談到模式窗體的打開一般通過FormShowDialog ()方法或她的一個重載FormShowDialog (IWinWindow)來實現其中後一個方法將窗體顯示為具有指定所有者的模式對話框如下代碼所示
OptionForm form = new OptionForm();
//formShowDialog();
formShowDialog(this);

對於指定所有者方式打開的模式窗體可以在模式窗體內部獲取主窗體的引用
//在模式窗體內部訪問所屬窗體
MainForm form = thisOwner as MainForm;
注意如果以FormShowDialog ()方式打開那FormOwner屬性會是空引用

談到模式窗體的關閉先來看一下模式窗體關閉後的返回值無論是調用FormShowDialog ()方法還是FormShowDialog (IWinWindow)方法都會在模式窗體關閉時返回SystemWindowsFormsDialogResult枚舉值參考MSDN該枚舉包含的值如下
DialogResultAbort對話框的返回值是 Abort(通常從標簽為中止的按鈕發送)
DialogResultCancel對話框的返回值是 Cancel(通常從標簽為取消的按鈕發送)
DialogResultIgnore對話框的返回值是 Ignore(通常從標簽為忽略的按鈕發送)
DialogResultNo對話框的返回值是 No(通常從標簽為的按鈕發送)
DialogResultNone從對話框返回了 Nothing這表明有模式對話框繼續運行
DialogResultOK對話框的返回值是 OK(通常從標簽為確定的按鈕發送)
DialogResultRetry對話框的返回值是 Retry(通常從標簽為重試的按鈕發送)
DialogResultYes對話框的返回值是 Yes(通常從標簽為的按鈕發送)
由於某些原因在實際用戶操作中比如選項數據無法保存輸入的設置數據有問題點擊[確定]按鈕需要阻止窗體的關閉以對輸入的設置進行調整對於一些開發者在技術社區貼的阻止模式窗體關閉的代碼我認為不是很好的實現以下用代碼來描述該實現注意其中用到了三個事件
//注冊窗體關閉事件
thisFormClosing += new SystemWindowsFormsFormClosingEventHandler(thisOptionForm_FormClosing);
//注冊確定按鈕事件
thisbuttonOKClick += new SystemEventHandler(thisbuttonOK_Click);
//注冊取消按鈕事件
thisbuttonCancelClick += new SystemEventHandler(thisbuttonCancel_Click);

  三個事件對應的事件處理程序如下
//確定按鈕處理程序
private void buttonOK_Click(object sender EventArgs e)
{
    //假設textBoxPath用來記錄目錄路徑如果不存在要求用戶重新設置
    if (thistextBoxPathTextTrim()Length == )
    {
        MessageBoxShow(輸入路徑信息不對!);
        thistextBoxPathFocus();
    }
    else
    {
        thisDialogResult = DialogResultOK;
    }
}
//取消按鈕處理程序
private void buttonCancel_Click(object sender EventArgs e)
{
    thisDialogResult = DialogResultCancel;
}
//窗體關閉處理程序在關閉窗體時發生
private void OptionForm_FormClosing(object sender FormClosingEventArgs e)
{
    if (thisDialogResult != DialogResultCancel && thisDialogResult != DialogResultOK)
        eCancel = true;
}

上面的代碼都正常就是事件寫多了對上面代碼進行修改去掉[取消]按鈕事件和窗體關閉事件以及相關的事件處理程序首先需要在窗體構造函數中通過設置按鈕的DialogResult屬性來實現返回特定的DialogResult
thisbuttonOKDialogResult = SystemWindowsFormsDialogResultOK;
thisbuttonCancelDialogResult = SystemWindowsFormsDialogResultCancel;

注冊確定按鈕事件
//注冊確定按鈕事件
thisbuttonOKClick += new SystemEventHandler(thisbuttonOK_Click);
//確定按鈕處理程序
private void buttonOK_Click(object sender EventArgs e)
{
    if (thistextBoxPathTextTrim()Length == )
    {
        MessageBoxShow(輸入的路徑信息不對!);
        thistextBoxPathFocus();
        //設置文本框焦點
        thisDialogResult = DialogResultNone;
    }
}
可見新的實現方式代碼減少了一半 

窗體的參數傳遞
對於窗體間的數據傳遞是剛開始從事Net窗體應用程序開發人員碰到的一個常見問題在此講幾個常見的實現方式此節內容適用於模式窗體或非模式窗體部分方式延伸到一般類的操作

)構造函數參數傳遞
通過構造函數傳遞參數應該是比較基本的參數傳遞方式重載構造函數通過帶參數的構造函數來實例化窗體
在窗體類內部定義參數變量
private object myParams;

實現構造函數
public OptionForm(object parameters)
{
    InitializeComponent();
    thismyParams = parameters;//設置參數引用
}

實例化窗體
OptionForm form = new OptionForm( myParams );

在實際使用過程中需要注意傳入的是引用類型還是值類型處理方式會有所不同

)使用窗體的屬性
說起屬性關聯上面已經提到過FormOwner屬性下面筆者根據MSDN文檔來比較完整的講一下大部分的文字來自MSDN文檔為保證其完整性對其中一些屬性描述進行了擴展

FormOwner 屬性獲取或設置擁有此窗體的窗體
語法public Form Owner { get; set; }
若要使某窗體歸另一個窗體所有可為其 Owner 屬性分配一個對將成為所有者的窗體的引用當一個窗體歸另一窗體所有時它便隨著所有者窗體最小化和關閉例如如果 Form 歸窗體 Form 所有則關閉或最小化 FormForm 也會關閉或最小化並且附屬窗體從不顯示在其所有者窗體後面可以將附屬窗體用於查找和替換窗口之類的窗口當選定所有者窗體時這些窗口不應消失
FormOwnedForms 屬性獲取 Form 對象的數組這些對象表示此窗體擁有的所有窗體
語法public Form[] OwnedForms { get; }
此屬性返回包含此窗體擁有的所有窗體的數組要使某窗體歸另一個窗體所有可調用 AddOwnedForm 方法分配給所有者窗體的窗體將保持被擁有狀態直到調用了 RemoveOwnedForm 方法如果窗體是多文檔界面 (MDI) 父窗體則除了當前打開的所有 MDI 子窗體外此屬性將返回所有顯示的窗體
FormMdiChildren 屬性獲取窗體的數組這些窗體表示以此窗體作為父級的多文檔界面 (MDI) 子窗體
語法public Form[] MdiChildren { get; }
此屬性使您得以獲取對當前在某 MDI 父窗體中打開的所有 MDI 子窗體的引用若要創建 MDI 子窗體請將要成為 MDI 父窗體的 Form 分配給該子窗體的 MdiParent 屬性可以使用此屬性依次通過所有 MDI 子窗體以執行一些操作如當 MDI 父窗體關閉時將數據保存到數據庫中或者根據應用程序中執行的操作更新子窗體上的字段
FormMdiParent 屬性獲取或設置此窗體的當前多文檔界面 (MDI) 父窗體
語法public Form MdiParent { get; set; }
若要創建 MDI 子窗體請將要成為 MDI 父窗體的 Form 分配給該子窗體的 MdiParent 屬性可以從某 MDI 子窗體使用此屬性來獲取所有子窗體都需要的全局信息或者調用對所有子窗體執行操作的方法
FormActiveForm 靜態屬性獲取此應用程序的當前活動窗體
語法public static Form ActiveForm { get; }
表示當前活動窗體或者如果沒有活動窗體則為空引用可以使用此方法獲得對當前活動窗體的引用以在該窗體或其控件上執行操作
FormActiveMdiChild 屬性獲取當前活動的多文檔界面 (MDI) 子窗口
語法public Form ActiveMdiChild { get; }
返回表示當前活動的 MDI 子窗口的 Form或者如果當前沒有子窗口則返回 空引用可使用此方法確定 MDI 應用程序中是否有任何打開的 MDI 子窗體也可使用此方法從 MDI 子窗口的 MDI 父窗體或者從應用程序中顯示的其他窗體對該 MDI 子窗口執行操作
ContainerControlParentForm 屬性獲取將容器控件分配給的窗體
語法public Form ParentForm { get; }
將容器控件分配給的 Form

以上屬性MSDN提供相應的代碼事例可直接拿來調試使用

  ()使用公共屬性
使用公共屬性也是一種比較常用的方式通過窗體設計器添加的控件默認訪問修飾符為private級別可以設置成public或Internal(在程序集內部可見)來對外公開比如對窗體中的Button進行公開那就可以訪問Button的相關屬性同時也可以注冊事件或撤銷事件注冊
OptionForm form = new OptionForm();
formbuttonOKClick += new EventHandler(buttonOK_Click);
formShowDialog();

對於只允許讀取訪問或修改訪問的控件或變量可以通過屬性來控制對()方式進行修改去除重載構造函數增加屬性也可以實現同樣的效果
public object MyParams
{
    get { return thismyParams;  }
    set { thismyParams = value;  }
}

)使用公共方法
使用公共方法類似於屬性對上面的同等實現如下
//獲取參數
public object GetParams()
{
    return thismyParams;
}
//設置參數
public void SetParams(object myParams )
{
    thismyParams = myParams;
}

)使用靜態類該方式可以簡單的理解為靜態變量全局共享通過下面代碼能夠比較清楚的理解先來定義靜態類
public static class ParameterSettings
{
    //公共靜態變量
    public static string Username = Zhengzuo;
    //私有靜態變量
    private static string userRole = Administrators;
    //私有靜態變量
    private static string password = ;
    //內部屬性
    internal static string UserRole
    {
        get { return userRole; }
    }
    //公共屬性
    public static string Password
    {
        get { return password; }
        private set { password = value; }
    }
}

在需要訪問的地方通過以下方式進行
string username = ParameterSettingsUsername;
string password = ParameterSettingsPassword;
string userRole = ParameterSettingsUserRole;
ParameterSettingsUsername = 鄭佐;//修改成新用戶名

)窗體實現Singleton模式
Singleton模式是我們開發過程中最常用的模式之一在技術社區經常看到有人談及對主窗體實現Singleton但個人認為這不是一種妥當的做法因為沒有這個必要這裡通過另一個自定義類來進行演示假設UserLoginInfo類用來保存登錄系統後的用戶憑據
/*==============================================
程序 鄭佐
==============================================*/
public class UserLoginInfo
{
    //實現Singleton模式線程安全
    private readonly static UserLoginInfo currentUserInfo = new UserLoginInfo();
    //提供全局訪問點
    public static UserLoginInfo CurrentUserInfo
    {
        get { return currentUserInfo; }
    }
    //阻止顯式實例化但不能阻止反射方式調用
    private UserLoginInfo()
    {
    }
    //公共變量
    public string Username;
    //私有變量
    private static string userRole;
    //私有變量
    private static string password;
    //內部屬性
    internal string UserRole
    {
        get { return userRole; }
        set { userRole = value; }
    }
    //公共屬性
    public string Password
    {
        get { return password; }
        internal set { password = value; }
    }
}

在其他代碼中進行訪問
UserLoginInfoCurrentUserInfoUsername =鄭佐;
UserLoginInfoCurrentUserInfoUserRole = dotnetlover;
UserLoginInfoCurrentUserInfoPassword = ;

對於Singleton模式的實現方式有很多編寫時需要考慮是否需要保證實例訪問的線程安全問題以免引發不可預料的情況為了提高性能可以考慮惰性實例化關於Singleton模式的更多信息可以參考另一篇文章

)發布事件進行訂閱
通過事件來傳遞參數應該說是一種推的實現方式在產生事件時進行被動的獲取相關數據這裡將通過一個自定義事件來演示數據的傳輸
在自定義事件時標准的做法都會先定義一個事件參數類要麼直接使用基類EventArgs或者從EventArgs繼承實現自己的參數類假設自定義基類取名為OptionSettingEventArgs
//選項設置事件參數類
public class OptionSettingEventArgs : EventArgs
{
    private string changedPath;
    //構造函數
    public OptionSettingEventArgs(string changedPath)
    {
        thischangedPath = changedPath;
    }
    //讀取參數
    public string ChangedPath
    {
        get { return thischangedPath; }
    }
}

以上參數類只包含一個修改後的路徑參數接下去我們要對原先的OptionForm窗體增加事件定義這裡使 中提供的泛型類來實現
//定義事件
public event EventHandler<OptionSettingEventArgs> OptionSettingChanged;
編寫事件引發程序如下
//引發OptionSettingChanged事件
protected virtual void OnOptionSettingChanged(OptionSettingEventArgs e)
{
    if (OptionSettingChanged != null)
    {
        OptionSettingChanged(this e);
    }
}

對文件目錄選擇按鈕事件處理程序進行修改來實現事件激發並沒有考慮直接從文本框直接數據輸入方式
//通過目錄對話框設置新的路徑
private void buttonBrowser_Click(object sender EventArgs e)
{
    FolderBrowserDialog dialog = new FolderBrowserDialog();
    DialogResult result = dialogShowDialog(this);
    if (result == DialogResultOK)
    {
        if(thistextBoxPathText != dialogSelectedPath)
        {
            thistextBoxPathText = dialogSelectedPath;
            OptionSettingEventArgs args = new OptionSettingEventArgs(dialogSelectedPath);
            OnOptionSettingChanged(args);
        }
    }
}

  好了一切准備工作完成調用代碼如下
OptionForm form = new OptionForm();
//注冊事件
formOptionSettingChanged += new EventHandler (form_OptionSettingChanged);
formShowDialog();
通過以下事件處理程序來驗證其正確性
private void form_OptionSettingChanged(object sender OptionSettingEventArgs e)
{
    string newPath = eChangedPath;
    MessageBoxShow(this StringFormat(新路徑為{} newPath) 提示);
}

在實際開發過程中合理的處理方式可能是以上幾種方式的組合對於窗體間的參數傳遞我在另一篇文章中也有比較多的基礎實例講解

Net Framework提供的模式窗體 < Framework為我們提供了一些比較常用的對話框,在開發過程中省了不少事,以下對其進行介紹。tW.wingwit.cOM
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);方法以模式方式打開窗體。

更多資源
對於windowsforms的開發,這裡推介幾本大師寫的書,
Charles Petzold著的《Programming Windows with C# (Core Reference)》,中文翻譯版為《Microsoft c#Windows程序設計(上下冊)》。
Chris Sells著的《Windows Forms Programming in C#》,中文翻譯版為《Windows Forms程序設計》。在上看到該書好像已經出第二版了。


From:http://tw.wingwit.com/Article/program/net/201311/11487.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.