12. 對話方塊、視窗和其他表單

本章要介紹一些和表單有關的程式設計技巧,這些技巧有些是已經常常在用的,例如在螢幕上擺設表單的位置,有些則是新工具所需要用到的技巧,如建立一個包含頁籤(Tab)的表單。這些設計技巧會讓你的程式更出色。

如何在專案中加入標準的「關於」對話方塊?
 

「關於」對話方塊是Windows應用程式中常見的一個標準元件,使用者經常可以從「說明」功能表中選擇「關於」選項,讓這個對話方塊出現,如果多觀察幾個Windows 95的軟體,你會發現它們在「說明」功能表中大部分都有「關於」選項。

你也可以加入「關於」對話方塊到應用程式裡,如果不求花俏繁複,最簡單的「關於」對話就是用MsgBox函式建立一個訊息方塊;如果要多一些花樣的話,可以設計一張表單作為你的「關於」對話方塊。

我們已經建立好一個Name屬性為frmAbout的表單而且存成ABOUT.FRM,它可以很容易地被載入你到的專案裡。這張表單上有三個標籤控制項,分別叫lblHeading、lblApplication和lblCopyright,以及一個叫cmdOK的指令按鈕。當表單被呼叫時,它看起來會類似圖12-1所顯示的表單,但表單上的文字內容則要看你傳入什麼樣的資料。


 

 圖12-1 標準的「關於」對話方塊

以下就是About表單的程式內容:

Option Explicit

Private Sub cmdOK_Click()
    `Cancel About form
    Unload Me
End Sub

Private Sub Form_Load()
    `Center this form
    Left = (Screen.Width - Width) \ 2
    Top = (Screen.Height - Height) \ 2
    `Set defaults
    lblApplication.Caption = "- Application -"
    lblHeading.Caption = "- Heading -"
    lblCopyright.Caption = "- Copyright -"
End Sub

Public Sub Display()
    `Display self as modal
    Show vbModal
End Sub

Property Let Heading(Heading As String)
    `Define string property for Heading
    lblHeading.Caption = Heading
End Property

Property Let Application(Application As String)
    `Define string property for Application
    lblApplication.Caption = Application
End Property

Property Let Copyright(Copyright As String)
    `Build complete Copyright string property
    lblCopyright.Caption = "Copyright (c) " & Copyright
End Property

我們用三個Let屬性程序來指定字串給lblHeading、lblApplication和lblCopyright的Caption屬性,只要呼叫Heading、Application和Copyright,就可以改變表單上的文字內容。如果要指定"ABOUTDEM"給lblApplication的Caption屬性,本來我們要用的程式碼是這樣的:

frmAbout.lbl.Application.Caption = "ABOUTDEM"

有了Application屬性程序後,程式可以簡化成:

frmAbout.Application = "ABOUTDEM"

使用屬性程序的另一個好處是:當屬性被設定時,我們可以做一些其他的動作。例如,Copyright屬性程序事實上做了一個字串連接的動作,而不只是單純的接受一個字串。

這張「關於」表單裡有一個公用方法Display,它用來取代Show方法。在Display方法裡我們用vbModal來呼叫Show方法,因此,在呼叫用Display方法時,不需要任何引數。配合著vbModal常數使用的Show方法會讓表單成為一個強制回應表單(Modal Form),使用者必須對這張表單有所回應(如按下「確定」鈕),讓表單消失之後,才能使用程式中的其他部份。標準的「關於」表單就是一種強制回應表單,因此,如果用Display方法來顯示表單,你可以不用顧慮表單在顯示時是一般表單還是強制提示表單。

你也可以在Display方法中加入其他程式碼,以加強表單的功能。例如,你可以加入一個計時器控制項(Timer Control)來控制表單停留在螢幕上的時間。總而言之,OOP的原則是讓物件本身去完成該完成的事,不用讓使用者擔心細節。這個Display方法就是遵循這個原則而設計出來的產物。

如果要設計一個不同風格的「關於」表單,你可以盡量去修改以上的程式,而且要記得把表單上的預設值改成你所需要的設定值,這些預設值在Form_Load事件程序裡可以看到。

筆者在本書第三部份的範例程式中用了許多上述的「關於」對話方塊。把這個對話方塊放進這些範例裡很容易:在標準的「說明」功能表的選項中加入一個「關於」選項,加入幾行程式讓對話方塊出現。如果你要用這種方式來呼叫「關於」對話方塊,首先要建立一個新專案,把ABOUT.FRM加到新專案裡,為專案的表單設計一個功能表,讓功能表的事件程序呼叫對話方塊。以下的程式就是功能表的事件程序,它所輸出的結果會和圖12-1一樣。

Option Explicit

Private Sub mnuAbout_Click()    
    `Set properties
    About.Heading = "Microsoft Visual Basic 6.0 Developer's " & _
        "Workshop"
    About.Application = "ABOUTDEM"
    About.Copyright = "1998 John Clark Craig and Jeff Webb"
    `Call a method
    About.Display
End Sub

「關於」對話方塊表單的範本(Template)
 

Visual Basic提供了許多表單的範本,讓你可以很容易地修改範本表單,然後加到你的應用程式裡。在Visual Basic提供的範本表單裡,其中一個就是「關於」對話表單。這個範本表單的一個特色是它提供了一個「系統資訊」按鈕,這個功能是許多Microsoft軟體的「關於」對話方塊中都會提供的,不需要去變更任何有關於「系統資訊」的程式。這些範本表單被存放在Visual Basic目錄下的TEMPLATE次目錄裡。

如果想要加入「關於」對話方塊到你的專案裡,請從「專案」功能表中選擇「新增表單」,叫出「新增表單」對話方塊,如圖12-2。

在「新增表單」對話方塊中選取「關於對話方塊」,按下「開啟」,那麼「關於我的應用程式」表單就會被加入到專案裡了。以下的程式在說明如何修改這個範本表單:


 

 圖12-2 「新增表單」對話方塊
Option Explicit

Private Sub mnuAbout_Click()    
    frmAbout.Caption = App.Title
    frmAbout.lblTitle = "ABOUTDEM"
    frmAbout.lblVersion = "Version " & App.Major & "." & App.Minor _
        & "." & App.Revision
    frmAbout.lblDescription = "Microsoft Visual Basic 6.0 " & _
        "Developer's Workshop"
    frmAbout.lblDisclaimer = "Copyright (c) 1998 John Clark Craig " & _
        "and Jeff Webb"
    frmAbout.Show
End Sub

圖12-3顯示了程式執行的結果。


 

 圖12-3 用Visual Basic的「關於」對話方塊範本表單

參考資料:

請參閱 第三十四章"進階應用程式" 中所介紹的「關於」對話方塊。


如何讓表單自動地在螢幕上定位?
 

如果要處理表單在螢幕上的定位問題,最好的處理方式是在表單的Form_Load件程序中撰寫表單定位的程式碼,這樣可以在表單被顯示之前就先把表單定位妥當。要把表單定位在螢幕中央,只要用兩行程式碼來計算和設定表單左上角的位置即可,請看以下的程式碼:

Private Sub Form_Load()
    'Center this form
    Left = (Screen.Width - Width) \ 2
    Top = (Screen.Height - Height) \ 2
End Sub

注意:

反斜線(\)用來作整數的除法,整數除法比浮點數除法快,而且在多數情形下,計算的結果一定要進位成整數。如果使用 \ 和使用 / 的結果都一樣,你應該選擇整數除法。


圖12-4所顯示的是在螢幕中央定位的表單。


 

 圖12-4 在螢幕中央定位的表單

如果要把表單放在螢幕上的其他位置,你最好能用一個專用的程序來處理這個問題。例如,以下的Locate程序所做的動作是:用實際的座標值,把表單的中央點定位在任何一個你想要的位置。

Private Sub Form_Load()
    `Position this form in left quarter, top third of screen
    Locate 1/4, 1/3
End Sub

Private Sub Locate( _
    Optional Xadjust As Single = 0.5, _
    Optional Yadjust As Single = 0.5 _
)
    Left = Xadjust * Screen.Width - Width \ 2
    Top = Yadjust * Screen.Height - Height \ 2
End Sub

參數Xadjust和Yadjust是螢幕尺寸的百分比:如果呼叫這個程序時不用任何引數,那麼預設值會被使用,表單就會被置放在螢幕的正中央。

另一個讓表單定位的技巧是使用Visual Basic提供的新功能──「表單配置」視窗,如圖12-5所示。如果「表單配置」視窗目前不在你的發展環境中,可以從「檢視」功能表中選擇「表單配置視窗」。


 

 圖12-5 「表單配置」視窗

在「表單配置」視窗中,你可以看到表單在螢幕上的位置,如果按下滑鼠右鍵,並且選擇「解析度指引」,可以看到代表不同解析度(640x480,800x600和1024x768)的虛線。雖然這是一個非常簡便的方法,但是仍然不能讓表單的位置不受螢幕解析度的影響,如果要讓螢幕上的表單在任何一種解析度設定下都在螢幕中央,你應該使用前述的程式。


參考資料:

請參閱 第三十四章"進階應用程式" ,這裡對表單定位技巧提供了進一步的討論。


如何產生一個上浮的視窗?
 

由Visual Basic表單所產生的視窗可以有好幾模式:強制回應模式(Modal Mode)、上浮模式(Floating Mode)和最上層模式(Topmost Mode),這幾種視窗都會被顯示在其他任何視窗的上面。在這一節裡,我們將一一討論各種視窗模式。

強制回應模式
 

如果用vbModal常數呼叫Show方法來顯示某張表單,當表單出現之後,使用者一定要對這張表單有回應,否則滑鼠游標離開了這張表單,應用程式其他的部份不會對滑鼠或鍵盤事件有任何反應,這種視窗模式稱之為強制回應模式,也可以稱為應用程式強制回應模式(Application Modal)。以下是使用強制回應模式表單的程式碼:

frmTest.Show vbModal

請注意,當使用者對表單回應之後(如按下「確定」鈕),你必須讓表單能夠隱藏或消失,否則你的應用程式無法進行其他的工作。

上浮模式
 

筆者認為上浮模式與最上層模式有所不同,你可以把上浮模式視窗想像成每隔一段時間它就會自動浮到最上層的視窗。要建立這種表單,你需要把一個Timer控制項加到表單中,然後把Timer控制項的Interval屬性設定為視窗上浮的時間間隔。我們選擇設定500毫秒(0.5秒,這是一個很合理的間隔)。接下來,把下面的程式碼加入到Timer1_Timer事件程序中,用ZOrder方法強迫視窗浮到最上層:

Private Sub Timer1_Timer
Zorder
End Sub

最上層模式
 

當你在不同的應用程式之間相互切換時,最上層模式視窗會使表單保持停留在所有視窗的上面。你可以用Windows API函式SetWindowPos,使用這種方式會比上述的上浮模式視窗的效果還好,而且Windows會幫你處理所有低階的工作。我們把SetWindowPos函式包裝在OnTop屬性中,以下的程式在Form_Load和Form_Unload事件程序中呼叫OnTop屬性:

`SetWindowPos flags
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOZORDER = &H4
Private Const SWP_NOREDRAW = &H8
Private Const SWP_NOACTIVATE = &H10
Private Const SWP_FRAMECHANGED = &H20
Private Const SWP_SHOWWINDOW = &H40
Private Const SWP_HIDEWINDOW = &H80
Private Const SWP_NOCOPYBITS = &H100
Private Const SWP_NOOWNERZORDER = &H200
Private Const SWP_DRAWFRAME = SWP_FRAMECHANGED
Private Const SWP_NOREPOSITION = SWP_NOOWNERZORDER

`SetWindowPos hwndInsertAfter values
Private Const HWND_TOP = 0
Private Const HWND_BOTTOM = 1
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2

Private Declare Function SetWindowPos _
Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal hWndInsertAfter As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal cx As Long, _
    ByVal cy As Long, _
    ByVal wFlags As Long _
) As Long

Private mbOnTop As Boolean
`~~~.OnTop 
Private Property Let OnTop(Setting As Boolean)
`Set form's OnTop property
    If Setting Then
        `Make this form topmost
        SetWindowPos hwnd, HWND_TOPMOST, _
            0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
    Else
        `Make this form non-topmost
        SetWindowPos hwnd, HWND_NOTOPMOST, _
            0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
    End If
    mbOnTop = Setting
End Property

Private Property Get OnTop() As Boolean
    `Return the private variable set in Property Let
    OnTop = mbOnTop
End Property

Private Sub Form_Load()
    `Place form on top of all others
    OnTop = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
    `Put form back in normal ZOrder
    OnTop = False
End Sub

在Form_Unload事件程序中,我們用一行程式碼把OnTop設定為False,以便讓表單回復原來的ZOrder狀態,事實上,你可以在任何你覺得有需要的地方把OnTop設定為False。舉例來說,你可以在適當的地方用這行程式,讓你的表單在最上層模式和其他模式之間相互切換。

在屬性程序中呼叫SetWindowPos使你更容易切換模式設定,下這行程式就可以在兩種模式之間相互切換:

OnTop = Not OnTop

你不需要把所有視窗定位的常數都放在程式裡,我們已經把重要的常數定義在程式裡了,你可以用Or運算子把這些常數和SetWindowPos的Flags參數合併在一塊,產生其他的視窗屬性。

如何建立一個商標畫面?
 

每當應用程式開始啟動時,會花一定的啟動時間,啟動時間的長短會和使用者系統的速度以及初始化動作的多寡成正比。通常一個較大的的應用程式會需要較長的啟動時間,在這種情況下,我們可以在螢幕上顯示一個啟動畫面,讓使用者不致於感覺等得太久。以下的程式用來顯示一個frmSplash表單,做為程式啟動時的商標畫面。

Option Explicit
Private Sub Form_Load()
    'Show this form
    Show
    'Show splash screen
    frmSplash.Show
    DoEvents
    'Perform time-consuming initializations...
    Initialize
    'Erase splash screen
    Unload Splash
End Sub

上面這段程式碼應該要被放在應用程式的啟動表單內。通常啟動表單都是在程式執行過程中一直會存在的表單,所有控制啟動畫面的程式碼,都應該放在啟動表單的Form_Load事件程序裡。在上面的程式中,第一個Show方法強制使啟動表單首先被顯示出來(通常這個顯示表單的動作會一直等到整個Form_Load程序結束後才會起作用),第二個Show方法則會顯示啟動畫面表單frmSplash,緊接在其後的DoEvents函式則用來確使啟動畫面的每一個部份都能夠立刻整地顯示出來。DoEvents函式實際上會強制使Visual Basic交出系統控制權,一直等到系統中所有等待完成的動作都結束後才把控制權還給Visual Basic。

程式中的Initialize函式所代表的是一連串相當耗時的初始化動作,如讀入資料檔、讀入資源檔(Resource File)、載入表單等等。當初始化工作完成後,我們就可以關閉這個畫面,而這時候應用程式也已經準備就緒,可以讓使用者使用了。

圖12-6顯示的是一個啟動畫面的例子。

啟動畫面的範本表單
 

前面我們提到過Visual Basic提供了許多範本表單,這些表單裡其中一個就是啟動畫面的範本表單。如果要加入啟動畫面到你的應用程式裡,從「專案」功能表中選取「新增表單」,在「新增表單」對話方塊中選取「啟動畫面」圖像,然後按下「開啟舊檔」按鈕,啟動畫面就會被加入到你的應用程式裡了。


 

 圖12-6 執行中的啟動畫面

以下的程式告訴你要如何修改啟動畫面的範本表單:

Option Explicit

Private Sub Form_Load()
    `Specify splash screen labels
    frmSplash.lblLicenseTo = App.LegalTrademarks
    frmSplash.lblCompanyProduct = App.ProductName
    frmSplash.lblPlatform = "Windows 95"
    frmSplash.lblCopyright = App.LegalCopyright
    frmSplash.lblCompany = App.CompanyName
    frmSplash.lblWarning = " Warning: This program is protected " & _
        "by copyright law, so don't copy" 
    `Show splash screen
    frmSplash.Show
    DoEvents
    `Perform time-consuming initializationsDear John, How Do I... 
    Initialize
    `Erase splash screen
    Unload frmSplash
End Sub

在上面的程式中,我們用App物件來取得有關應用程式本身的資訊,這些資訊大部份都是在「專案屬性」對話方塊內的設定值,你可以從「專案」功能表中選取「專案屬性」,叫出這個對話方塊。

啟動畫面範本表單的程式模組內容如下:

Private Sub Form_KeyPress(KeyAscii As Integer)
    Unload Me
End Sub

Private Sub Form_KeyPress(KeyAscii As Integer)
    Unload Me
End Sub

Private Sub Form_Load()
    lblVersion.Caption = "Version " & App.Major & "." & _
        App.Minor & "." & App.Revision
    lblProductName.Caption = App.Title
End Sub

Private Sub Frame1_Click()
    Unload Me
End Sub

範本表單的模組已經包含了版本和產品名稱等相關的程式碼。

圖12-7顯示了程式輸出的結果。


 

 12-7 用範本表單所產生的啟動畫面

參考資料:

請參閱 第三十二章"資料庫" 中的Jot應用程式,這裡對啟動畫面會作進一步的展示說明。


如何使用頁籤控制項?
 

Visual Basic 6提供了兩種頁籤控制項:TabStrip控制項(儲存在COMCTL32.OCX檔中),以及SSTab控制項(在TABCTL32.OCX檔中),其中SSTab是Visual Basic 4中Sheridan SSTab控制項的改良版。

你可以從「設定使用元件」對話方塊中,選取Microsoft Windows Common Controls加入TabStrip控制項或選取Microsoft Tabbed Dialog Control加入SSTab控制項到工具箱中。

SSTab控制項
 

SSTab控制項比TabStrip控制項更容易使用,這兩者主要的差別是:使用TabStrip控制項時,通常都得為每一個頁籤加入一個收納器控制項,如框架(Frame)或圖片方塊(PictureBox)等,然後在收納器控制項裡放進要用到的控制項,最後加入顯示每個收納器的程式碼。

有了SSTab控制項,如圖12-8,只要在一個頁籤中放進需要的控制項即可,當使用者選到某個頁籤時,頁籤的內容會自動顯示出來。在這裡要提醒你的是:必須在頁籤的範圍內畫出需要的控制項,不可以在頁籤範圍之外先畫出控制項,再把它移入頁籤範圍內。如果只是把某個控制項拖曳到頁籤裡,這個控制項會浮在SSTab控制項上面,和任何一個頁籤毫無關聯。


 

 圖12-8 執行中的SSTab控制項

現在的SSTab控制項包含了一個ToolTips屬性,這是Visual Basic 4版的SSTab所沒有的,另外,SSTab控制項也包含了兩個Style屬性的設定值:ssStyleTabbedDialog和ssStylePropertyPage。如果Style屬性被設定為ssStyleTabbedDailog,頁籤的外觀就會像是Window 3.1應用程式中使用的頁籤,而如果Style的設定值是ssStylePropertyPage,頁籤就會像是Windows 95應用程式中使用的頁籤。

最後值得一提的是,在設計16位元的應用程式時,不能使用Visual Basic 6的SSTab控制項,你只能使用Visual Basic 4所提供16位元版的SSTab控制項。

如何閃爍表單以引起使用者的注意?
 

如果要讓使用者把注意力集中到某張表單上,你可以用FlashWindow API函式來閃爍表單的標題區。現在請在一個專案裡建立兩張表單,frmControl和frmFlash,在frmFlash表單中加入一個Timer控制項,把它命名為tmrFlash,然後加入下列的程式碼到frmFlash表單內:

Option Explicit

Private Declare Function FlashWindow _
Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal bInvert As Long _
) As Long

Private Sub Form_Load()
    tmrFlash.Enabled = False
End Sub

Private Sub tmrFlash_Timer()
    Dim lngRtn As Long
    lngRtn = FlashWindow(hwnd, CLng(True))
End Sub

Property Let Rate(intPerSecond As Integer)
    tmrFlash.Interval = 1000 / intPerSecond
End Property

Property Let Flash(blnState As Boolean)
    tmrFlash.Enabled = blnState
End Property

接下來加入三個指令按鈕(cmdFast、cmdSlow和cmdStop)到frmControl表單中,把這三個命令按鈕的Caption屬性內容改為"Fast"、"Slow"和"Stop",最後在frmControl表單中加入以下的程式碼:

Option Explicit

Private Sub cmdFast_Click()
    frmFlash.Rate = 5
    frmFlash.Flash = True
End Sub

Private Sub cmdStop_Click()
    frmFlash.Flash = False
End Sub

Private Sub cmdSlow_Click()
    frmFlash.Rate = 1
    frmFlash.Flash = True
End Sub

Private Sub Form_Load()
    frmFlash.Show
End Sub

圖12-9顯示的是兩張表單在執行的狀態。


 

 圖12-9 frmControl表單控制frmFlash表單的閃爍動作

圖12-8顯示駐點(Focus)停留在frmControl表單上,而frmFlash表單的標題區在閃爍;如果可以看見Windows的工作列,你會發現工作列中的frmFlash圖像也跟著閃爍,甚至當其他視窗遮蓋住frmFlash時,Windows 95工作列上frmFlash的圖像也仍在閃爍。

我們在閃爍的表單中加入了Rate和Flash屬性。在應用程式中,你可以設定Flash的值為True或是False來決定表單是否要閃爍,並且設定某個整數給Rate來決定表單閃爍的頻率。請仔細看看frmControl中指令按鈕的程式碼,就可以了解如何使用Flash和Rate屬性。


參考資料:


如何在執行階段移動控制項到另一個收納器裡?
 

在Visual Basic中,大部份控制項的Container屬性都是可讀可寫的,利用這個特性,程式設計人員可以創造出極具創意的應用程式來。你可以改變某個控制項的Container屬性設定值,使得這個控制項可以由一個收納器跳到另一個收納器中。

現在我們來看看如何移動一個控制項到某個收納器裡。首先在一張空白表單上畫出兩個Frame控制項,fraLeft和fraRight,然後在fraLeft中畫出一個指令按鈕控制項,把它命名為cmdJump。圖12-10顯示的是這張表單的外觀。


 

 圖12-10 利用控制項的Container屬性移動控制項到一個收納器中

接下來,請加入這段程式到表單裡。

Option Explicit

Private Sub cmdJump_Click()
    Set cmdJump.Container = fraRight
End Sub

當程式執行時,按下cmdJump指令按鈕,它就會跳到右邊的框架裡。這個例子只是用來簡單地說明Visual Basic的靈活性。現在Visual Basic的物件變得比前更具動態性也更容易由程式所控制。