31.日期與時間

本章介紹幾個處理日期和時間資料的應用程式給各位作參考:VBCal提供一種方便使用者選擇日期的界面;VBClock顯示系統時間;NISTTime會自動撥號到美國國家標準及科技中心(National Institute of Standards and Technology,NIST),以準確地設定你的系統時間。

VBCal應用程式


在一般的商業應用程式中,讓使用者輸入日期是一件常見的工作,其中最簡單方法就是讓使用者自己打入日期到文字方塊中,然而這種方式需要花許多工夫在日期檢查和日期國際化等工作上,舉例來說,3/5/91是代表三月五日還是五月三日?為解決這許許多多的問題,最好方式就是顯示一張月曆,讓使用者直接點選月曆上的日期。VBCal應用程式會運用三種具備月曆界面的控制項提供方便的使用者界面。

VBCal Wizard
 

輔助精靈的概念是由微軟首次採用的,這是一種方便簡單的方法讓使用者能與系統一步一步地進行互動交談。我們把VBCal應用程式也設計成輔助精靈的形式。

VBCal應用程式包含五張表單,個別代表VBCal Wizard執行時的每一個步驟。VBCALWIZ.BAS模組只包含三行程式碼,其功用在於將使用者選擇的日期提供給各個表單使用。圖31-1到圖31-5是VBCal Wizard執行時每一張表單的情形。


 

 圖31-1 VBCal Wizard的第一步


 

 圖31-2 VBCal Wizard的第二步,以DTPicker控制項要求輸入第一個日期


 

 圖31-3 VBCal Wizard的第三步,以MonthView控制項要求輸入第二個日期


 

 圖31-4 VBCal Wizard的第四步,以Calendar控制項要求輸入第三個日期


 

 圖31-5 顯示最後的結果

DTPicker控制項
 

在VBCAL WIZARD的第二步中,使用者被要求以DTPicker控制項輸入一個日期。DTPicker控制項可以讓使用者鍵入一個日期或者由一個月曆視窗中選擇一個日期。DTPicker控制項的CustomFromat屬性讓你可以很有彈性地定義日期或時間的格式;而控制項本身會負責處理的細節包括自動驗證輸入的日期和顯示小月曆。如圖31-2所示,我們用傳統的日期格式要求使用者輸入日期。為了避免Y2K的問題,我們把年份格式設定為四位數。

MonthView控制項


VBCAL WIZARD的第三步以MonthView控制項讓使用者選擇第二個日期。你會發現MonthView控制項的小月曆和DTPicker控制項的月曆在外觀上很相似。圖31-3所顯示的是MonthView控制項執行時的情形。

這個控制項有幾個選擇讓你控制它的外觀和功能。例如,你可以把MonthColumns和MonthRows的值設定為大於1,這樣就可以同時顯示幾個月的月曆。

Calendar控制項
 

VBCAL WIZARD的第四步,如圖31-4,是用新的Calendar控制項讓使用者以另一種方式選擇日期。這個控制項也會顯示一個月曆界面,但是它有更多的屬性可以控制月曆的格式與外觀,而且月曆的大小可以任意改變。

我們在Calendar控制項旁邊加了一個Today按鈕,讓使用者可以直接跳到今天的日期。從程式碼中你會發現,我們讓使用者以點選兩次的方式來選定一個日期。圖31-6所顯示的是Calendar控制項執行的情形,其中2001年1月1日是選定的日期。


 

 圖31-6 執行中的Calendar控制項

VBCALWIZ.BAS


VBCal的所有表單透過三個公共變數gdtmDate1、gdtmDate2和gdtmDate3相互溝通,這三個變數都定義在VBCALWIZ.BAS模組裡。當使用者繼續下一步或是回到上一步時,這些變數都會記錄著在每一步中被選定的日期。

圖31-7所顯示的是VBCal應用程式的專案內容,其中表單frmVBCalWiz1被設定為起始表單。專案中大部份的檔案都用來定義精靈的每一個步驟,代表每一個步驟的各個表單,其大小都被設定為相同,並將指令按鈕定義在相同的位置上,這樣的設計可以讓使用者覺得他是在同一張表單上完成所有步驟。


 

 圖31-7 VBCal應用程式的專案內容

要建立VBCal應用程式的輔助精靈,請按照以下這些表來加入控制項,並加入後面的程式。

VBCAWIZ1.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBCalWiz1

VBCal Wizard - Step 1

3 - Fixed Dialog

 Image 
Name

Picture

Stretch

imgCal

VBCAL.BMP

True

 Label 
Name lblPrompt
 CommandButton 
Name

Caption

Default

cmdNext

&Next>

True

 CommandButton 
Name

Caption

Cancel

cmdCance

&Cancel

True

VBCAWIZ1.FRM原始程式碼
 

Option Explicit

Private Sub cmdCancel_Click()
    Unload Me
End Sub

Private Sub cmdNext_Click()
    frmVBCalWiz2.Show
    Unload Me
End Sub


Private Sub Form_Load()
    `Preset today's date
    If gdtmDate1 = 0 Then
        gdtmDate1 = Date
        gdtmDate2 = Date
        gdtmDate3 = Date
    End If
    `Display the prompting text
    lblPrompt.Caption = "This example wizard " & _
        "demonstrates three controls that let " & _
        "you select dates as it helps you " & _
        "calculate the number of days " & _
        "between two dates."
    `Center form
    Move (Screen.Width - Width) \ 2, _
         (Screen.Height - Height) \ 2
    Show
    cmdNext.SetFocus
End Sub
VBCAWIZ2.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBCalWiz2

VBCal Wizard - Step 2

3 - Fixed Dialog

 Image 
Name

Picture

imgCal

VBCAL.BMP

 Label 
Name

Caption

lblPrompt

Select the first date using the DTPicker control...

 CommandButton 
Name

Caption

Defaul

cmdNext

&Next>

True

 CommandButton 
Name

Caption

cmdBack

<&Back

 DTPicker 
Name

Format

CustomFormat

dtpDemo

3 - dtpCustom

/date format: mm/dd/yyyy...' MM/dd/yyyy

VBCAWIZ2.FRM原始程式碼
 

Option Explicit

Private Sub cmdBack_Click()
    gdtmDate1 = dtpDemo
    frmVBCalWiz1.Show
    Unload Me
End Sub

Private Sub cmdNext_Click()
    gdtmDate1 = dtpDemo
    frmVBCalWiz3.Show
    Unload Me
End Sub

Private Sub Form_Load()
    `Center form
    Move (Screen.Width - Width) \ 2, _
         (Screen.Height - Height) \ 2
    Show

    dtpDemo = gdtmDate1
    cmdNext.SetFocus
End Sub
VBCAWIZ3.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBCalWiz3

VBCal Wizard - Step 3

3 - Fixed Dialog

 Image 
Name

Picture

imgCal

VBCAL.BMP

 Label 
Name

Caption

lblPrompt

Select the second date using the MonthView control...

 CommandButton 
Name

Caption

Default

cmdNext

&Next>

True

 CommandButton 
Name

Caption

cmdBack

<&Back

 MonthView 
Name mvwDemo

VBCAWIZ3.FRM原始程式碼
 

Option Explicit

Private Sub cmdBack_Click()
    gdtmDate2 = mvwDemo.Value

    frmVBCalWiz2.Show
    Unload Me
End Sub

Private Sub cmdNext_Click()
    gdtmDate2 = mvwDemo.Value
    frmVBCalWiz4.Show
    Unload Me
End Sub

Private Sub Form_Load()
    `Center form
    Move (Screen.Width - Width) \ 2, _
         (Screen.Height - Height) \ 2
    Show
    mvwDemo.Value = gdtmDate2
    cmdNext.SetFocus
End Sub

Private Sub mvwDemo_DateDblClick(ByVal DateDblClicked As Date)
    `Allow double-click to select a date
    cmdNext_Click
End Sub
VBCAWIZ4.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBCalWiz4

VBCal Wizard - Step 4

3 - Fixed Dialog

 Image 
Name

Picture

imgCal

VBCAL.BMP

 Label 
Name

Caption

lblPrompt

Select a third date using the Calendar control...

 CommandButton 
Name

Caption

Default

cmdNext

&Next>

True

 CommandButton 
Name

Caption

cmdBack

<&Back

 CommandButton 
Name

Caption

cmdToday

&Today

 Calendar 
Name calDemo

VBCAWIZ4.FRM原始程式碼
 

Option Explicit

Private Sub calDemo_DblClick()
    `Allow double-click selection of date
    cmdNext_Click
End Sub

Private Sub cmdBack_Click()
    gdtmDate3 = calDemo.Value
    frmVBCalWiz3.Show
    Unload Me
End Sub

Private Sub cmdNext_Click()
    gdtmDate3 = calDemo.Value

    frmVBCalWiz5.Show
    Unload Me
End Sub

Private Sub cmdToday_Click()
    `Reset selected date to today
    calDemo.Value = Date
End Sub

Private Sub Form_Load()
    `Center form
    Move (Screen.Width - Width) \ 2, _
         (Screen.Height - Height) \ 2
    calDemo.Value = gdtmDate3
End Sub
VBCAWIZ5.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBCalWiz5

VBCal Wizard - Final Report

3 - Fixed Dialog

 Label 
Name lblReport
 CommandButton 
Name

Caption

cmdBack

<&Back

 CommandButton 
Name

Caption

cmdOK

&OK

VBCAWIZ5.FRM原始程式碼
 

Option Explicit

Private Sub cmdOK_Click()
    Unload Me
End Sub

Private Sub cmdBack_Click()
    frmVBCalWiz4.Show
    Unload Me
End Sub

Private Sub Form_Load()
    `Center this form
    Move (Screen.Width - Width) \ 2, _
         (Screen.Height - Height) \ 2
    lblReport.Caption = "Number of days separating " _
        & "these dates:" & vbCrLf & vbCrLf & "Between " _
        & Format$(gdtmDate1, "mm/dd/yyyy") & " and " _
        & Format$(gdtmDate2, "mm/dd/yyyy") & " is " _
        & Abs(gdtmDate1 - gdtmDate2) & vbCrLf & "Between " _
        & Format$(gdtmDate2, "mm/dd/yyyy") & " and " _
        & Format$(gdtmDate3, "mm/dd/yyyy") & " is " _
        & Abs(gdtmDate2 - gdtmDate3) & vbCrLf & "Between " _
        & Format$(gdtmDate3, "mm/dd/yyyy") & " and " _
        & Format$(gdtmDate1, "mm/dd/yyyy") & " is " _
        & Abs(gdtmDate3 - gdtmDate1)
End Sub

VBCALWIZ.BAS原始程式碼
 

Option Explicit

Public gdtmDate1 As Date
Public gdtmDate2 As Date
Public gdtmDate3 As Date

VBClock應用程式
 

VBClock應用程式的主要用途在於顯示系統上目前的時間;在這個應用程式裡,我們介紹幾種繪圖技巧,並且告訴你如何在應用程式中加入「關於」對話方塊,如圖31-8所示,VBClock表單的背景是一個點陣圖形,你可以在picBackGround圖片方塊控制項中載入你喜歡的圖案。


 

 圖31-8 執行中的VBClock應用程式

我們先介紹一下VBClock的主要功能。首先,「Help」功能表提供了「Contents」和「Search for Help On...」兩個選項來讀取說明檔,其次,「Option」功能表讓使用者可以設定時間以及選擇時鐘指針的顏色。我們用InputBox函式來設定時間,(你也可以用DTPicker控制項來設定時間),用一個對話方塊來改變指針顏色,在這張對話方塊表單中所使用是一個利用"焦點"(Hot Spot)的圖形技巧。


注意:

使用NISTTime應用程式可以更新你的系統時間,下一節我們會談到NISTTime,你可以同時執行NISTTime和VBClock,看看系統的時鐘如何進行對時校正。


圖31-9所顯示的是VBClock應用程式的專案結構。


 

 圖31-9 VBClock應用程式的專案視窗

VBCLOCK.FRM
 

VBClock表單顯示時鐘的圖像並且每秒鐘更新指針的位置一次,這張表單上只有三個控制項:用來顯示背景和指針的圖片方塊控制項,更新時鐘的計時器控制項,以及為說明檔提供界面的通用對話方塊控制項。


注意:

AutoRedraw是圖片方塊控制項一個很重要的屬性,為了要使時鐘指針跳動時顯得平順而俐落,最好要把這個屬性讀為True,如果設為Fasle,你可能會發現圖形在擦掉和重畫的時候發生閃動的情形。



 

 圖31-10 顯示設計階段的VBClock表單

請用以下兩張表和後面的程式來建立VBColck表單。

VBCLOCK.FRM功能表項目
標題 名稱 內縮 啟用
&File mnuFile 0 True
&New mnuNew 1 False
&Open... mnuOpen 1 False
&Save mnuSave 1 False
Save&As... mnuSaveAs 1 False
- mnuFileDash1 1 True
E&xit mnuExit 1 True
&Options mnuOption 0 True
&SetTime... mnuSetTime 1 True
&HandColors... mnuHandColors 1 True
&Help mnuHelp 0 True
&Contents mnuContents 1 True
&SearchForHelpOn... mnuSearch 1 True
- mnuHelpDash1 1 True
&About... mnuAbout 1 True
VBCLOCK.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

frmVBClock

VBClock

3 - Fixed Dialog

 Timer 
Name

Interval

tmrClock

100

 CommonDialog 
Name cdlOne
 PictureBox 
Name

AutoRedraw

AutoSize

Picture

picBackGround

True

True

MNDLBRT.BMP

VBCLOCK.FRM原始程式碼
 

Option Explicit

Public gintHourHandColor As Integer
Public gintMinuteHandColor As Integer
Public gintSecondHandColor As Integer
Private mintHnum As Integer
Private mintMnum As Integer
Private mintSnum As Integer
Private mlngHcolor As Long
Private mlMcolor As Long
Private mlngScolor As Long
Private msngHlen As Single
Private msngMlen As Single
Private msngSlen As Single
Private mstrAppname As String
Private mstrSection As String
Private mstrKey As String
Private mstrSetting As String

Private Const sngPi = 3.141593!
Private Const sngTwoPi = sngPi + sngPi
Private Const sngHalfPi = sngPi / 2

Private Sub Form_Load()
    `Fill form exactly with background image
    picBackGround.Move 0, 0
    Me.Width = picBackGround.Width + (Me.Width - ScaleWidth)
    Me.Height = picBackGround.Height + (Me.Height - ScaleHeight)
    `Change the scaling of the clock face
    picBackGround.Scale (-2, -2)-(2, 2)
    `Center form
    Me.Left = (Screen.Width - Me.Width) \ 2
    Me.Top = (Screen.Height - Me.Height) \ 2
    `Set width of hands in pixels

    picBackGround.DrawWidth = 5
    `Set length of hands
    msngHlen = 0.8
    msngMlen = 1.5
    msngSlen = 1
    `Set colors of hands from Registry settings
    mstrAppname = "VBClock"
    mstrSection = "Hands"
    mstrKey = "mlngHcolor"
    mstrSetting = GetSetting(mstrAppname, mstrSection, mstrKey)
    gintHourHandColor = Val(mstrSetting)
    mstrKey = "mlMcolor"
    mstrSetting = GetSetting(mstrAppname, mstrSection, mstrKey)
    gintMinuteHandColor = Val(mstrSetting)
    mstrKey = "mlngScolor"
    mstrSetting = GetSetting(mstrAppname, mstrSection, mstrKey)
    gintSecondHandColor = Val(mstrSetting)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    `Save current hand colors
    mstrKey = "mlngHcolor"
    mstrSetting = Str$(gintHourHandColor)
    SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting
    mstrKey = "mlMcolor"
    mstrSetting = Str$(gintMinuteHandColor)
    SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting
    mstrKey = "mlngScolor"
    mstrSetting = Str$(gintSecondHandColor)
    SaveSetting mstrAppname, mstrSection, mstrKey, mstrSetting
End Sub

Private Sub mnuAbout_Click()
    frmAbout2.Display

End Sub

Private Sub mnuExit_Click()
    Unload Me
End Sub

Private Sub mnuContents_Click()
    cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp"
    cdlOne.HelpCommand = cdlHelpContents
    cdlOne.ShowHelp
End Sub

Private Sub mnuHandColors_Click()
    `Show form for selecting hand colors
    frmVBClock2.Show vbModal
End Sub

Private Sub mnuSearch_Click()
    cdlOne.HelpFile = App.Path & "\..\..\Help\Mvbdw.hlp"
    cdlOne.HelpCommand = cdlHelpPartialKey
    cdlOne.ShowHelp
End Sub

Private Sub tmrClock_Timer()
    Dim dblHang As Double
    Dim dblMang As Double
    Dim dblSang As Double
    Dim dblHx As Double
    Dim dblHy As Double
    Dim dblMx As Double
    Dim dblMy As Double

    Dim dblSx As Double
    Dim dblSy As Double

    `Keep track of current second
    Static intLastSecond As Integer
    `Check to see if new second
    If Second(Now) = intLastSecond Then
        Exit Sub
    Else
        intLastSecond = Second(Now)
    End If
    `Update time variables
    mintHnum = Hour(Now)
    mintMnum = Minute(Now)
    mintSnum = Second(Now)
    `Calculate hand angles
    dblHang = sngTwoPi * (mintHnum + mintMnum / 60) / 12 - sngHalfPi
    dblMang = sngTwoPi * (mintMnum + mintSnum / 60) / 60 - sngHalfPi
    dblSang = sngTwoPi * mintSnum / 60 - sngHalfPi
    `Calculate endpoints for each hand
    dblHx = msngHlen * Cos(dblHang)
    dblHy = msngHlen * Sin(dblHang)
    dblMx = msngMlen * Cos(dblMang)
    dblMy = msngMlen * Sin(dblMang)
    dblSx = msngSlen * Cos(dblSang)
    dblSy = msngSlen * Sin(dblSang)
    `Restore background image
    picBackGround.Cls
    `Draw new hands
    picBackGround.Line (0, 0)-(dblMx, dblMy), _
        QBColor(gintMinuteHandColor)
    picBackGround.Line (0, 0)-(dblHx, dblHy), _
        QBColor(gintHourHandColor)
    picBackGround.Line (0, 0)-(dblSx, dblSy), _
        QBColor(gintSecondHandColor)
End Sub


Private Sub mnuSetTime_Click()
    Dim strPrompt As String
    Dim strTitle As String
    Dim strDefault As String
    Dim strStartTime As String
    Dim strTim As String
    Dim strMsg As String
    `Ask user for new time
    strPrompt = "Enter the time, using the format 00:00:00"

    strTitle = "VBClock"
    strDefault = Time$
    strStartTime = strDefault
    strTim = InputBox$(strPrompt, strTitle, strDefault)
    `Check if user clicked Cancel
    `or clicked OK with no change to time
    If strTim = "" Or strTim = strStartTime Then
        Exit Sub
    End If
    `Set new time
    On Error GoTo ErrorTrap
    Time = strTim
    Exit Sub
ErrorTrap:
    strMsg = "The time you entered is invalid. " + strTim
    MsgBox strMsg, 48, "VBClock"
    Resume Next
End Sub

為什麼tmrClock計時器的Interval屬性值被設為100微秒(1/10秒)而不1000微秒(1秒)呢?因為雖然在畫面上指針需要每秒更新一次,但設定Interval屬性為1000會造成在視覺上秒針跳動不順暢的缺點。這種跳動不順暢的情形之所以會發生,是因為Visual Basic計時器控制項所觸發的Timer事件,並不會準時地在時間間隔(Interval的值)到的時候發生,而是過了時間間隔之後才發生,所以才會引起Timer事件的驅動間隔產生輕微而不可預測的變動,(較快的電腦可能情況會比較好些),因而造成畫面上時鐘秒針隔秒跳動的情況發生。

為了解決這個問題,我們把計時器的Interval屬性設定為100微秒,比半秒還少,這樣就不會有指針跳動異常的現象發生。由於tmrClock的Interval屬性被設為100,Timer事件約每隔100微秒就會啟動一次,檢查前一次指針更新的時間和目前的時間,如果這間隔還不到一秒,程序立刻結束。這種"無效"的檢查使得系統整體的速度受到了小小的延遲,這是把Interval屬性值變成100的缺點。

使用系統登錄
 

VBClock應用程式允許使用者改變指針的顏色。為了使指針能夠使用前一次執行時的顏色,我們在Unload事件程序中把指針顏色的資訊存放在登錄裡,而在Load事件程序中從登錄檔案裡讀取前一次執行時指針的顏色。

VBCLOCK2.FRM
 

VBClock2表單提供一個圖形介面讓使用者可以從16種主要顏色中選擇三個指針的顏色,這樣的介面設計可以省掉描述顏色的文字,而從視覺上輔助使用者選擇顏色。圖31-11所顯示的是VBClock表單執行的情形。


 

 圖31-11 VBClock表單執行的情形

在表單的Load事件程序中,我們把16種顏色按深淺度分別放在圖片方塊的16個均分區域中,每個圖片方塊裡座標的Y軸(高)範圍是從0到1,而X軸(寬)是從0到16。無論圖片方塊的實際尺寸是多大,每一個圖片方塊都會被均分為16個區域,分別填上16種顏色。當使用者在圖片方塊的任一處按下滑鼠左鍵,滑鼠游標在X軸上的位置值就會被轉成0到15之間的一個整數(原本是Single),代表被選中的顏色。

圖形焦點
 

Click事件所提供的資訊並不足夠讓我們判斷Click事件發生時滑鼠游標的位置,因此,我們用MouseMove事件所提供的X參數,不斷地追蹤記錄著目前游標的位置,把游標位置的資訊存放在幾個全域變數裡(mintHourMouseX,mintMinuteMouseX,和mintSecondMouseX)。在Click事件發生時,這些變數裡的值,就決定了使用者所選擇的顏色。

這種技巧可以用來設計你圖形上的焦點(Hot Spot),不管你的圖形是圓型、多邊形或任何形狀都一律適用。

圖31-12顯示的是發展中的VBClock2表單。


 

 圖31-12 設計階段中的VBClock2表單

請依照以下這張表及後面的程式來建立VBColck2表單。

VBCLOCK2.FRM物件與屬性設定
編號 *  屬性
 Form  Name

Caption

BorderStyle

Icon

frmVBClock2

VBClock - Select Colors for Clock Hands

3 - Fixed Dialog

CLOCK02.ICO

 Label 

1

Name

Caption

Alignment

lblHourHand

Hour hand

1 - Right Justify

 Label 

2

Name

Caption

Alignment

lblMinuteHand

Minute hand

1 - Right Justify

 Label 

3

Name

Caption

Alignment

lblSecondHand

Second hand

1 - Right Justify

 PictureBox 

4

Name picHourColor
 PictureBox 

5

Name picMinuteColor
 PictureBox 

6

Name picSecondColor
 CommandButton 

7

Name

Caption

Default

cmdOK

OK

True

 CommandButton 

8

Name

Caption

Cancel

cmdCancel

Cancel

True

*"編號"欄中的號碼用來標示圖31-12中表單上物件的位置。

VBCLOCK2.FRM原始程式碼
 

Option Explicit

Private mintHourMouseX As Integer
Private mintMinuteMouseX As Integer
Private mintSecondMouseX As Integer
Private mintHourHand As Integer
Private mintMinuteHand As Integer

Private mintSecondHand As Integer

Private Sub cmdCancel_Click()
    `Cancel without changing hand colors
    Unload Me
End Sub

Private Sub cmdOK_Click()
    `Reset hand colors
    frmVBClock.gintHourHandColor = mintHourHand
    frmVBClock.gintMinuteHandColor = mintMinuteHand
    frmVBClock.gintSecondHandColor = mintSecondHand
    `Return to clock form
    Unload Me
End Sub

Private Sub Form_Activate()
    Form_Paint
End Sub

Private Sub Form_Load()
    `Get current hand colors
    mintHourHand = frmVBClock.gintHourHandColor
    mintMinuteHand = frmVBClock.gintMinuteHandColor
    mintSecondHand = frmVBClock.gintSecondHandColor
    `Scale picture boxes
    picHourColor.Scale (0, 0)-(16, 1)
    picMinuteColor.Scale (0, 0)-(16, 1)
    picSecondColor.Scale (0, 0)-(16, 1)
End Sub

Private Sub Form_Paint()
    Dim i As Integer
    `Draw the 16 colors in each color "bar"

    For i = 0 To 15
        `Draw colored boxes
        picHourColor.Line (i, 0)-(i + 1, 1), QBColor(i), BF
        picMinuteColor.Line (i, 0)-(i + 1, 1), QBColor(i), BF
        picSecondColor.Line (i, 0)-(i + 1, 1), QBColor(i), BF
    Next i
    `Draw check marks for current colors

    picHourColor.DrawWidth = 2
    picHourColor.Line (mintHourHand + 0.3, 0.5) _
        -(mintHourHand + 0.5, 0.7), QBColor(mintHourHand Xor 15)
    picHourColor.Line (mintHourHand + 0.5, 0.7) _
        -(mintHourHand + 0.8, 0.2), QBColor(mintHourHand Xor 15)
    picMinuteColor.DrawWidth = 2
    picMinuteColor.Line (mintMinuteHand + 0.3, 0.5) _
        -
(mintMinuteHand + 0.5, 0.7), QBColor(mintMinuteHand Xor 15)
    picMinuteColor.Line (mintMinuteHand + 0.5, 0.7) _
        -
(mintMinuteHand + 0.8, 0.2), QBColor(mintMinuteHand Xor 15)
    picSecondColor.DrawWidth = 2
    picSecondColor.Line (mintSecondHand + 0.3, 0.5) _
        -
(mintSecondHand + 0.5, 0.7), QBColor(mintSecondHand Xor 15)
    picSecondColor.Line (mintSecondHand + 0.5, 0.7) _
        -
(mintSecondHand + 0.8, 0.2), QBColor(mintSecondHand Xor 15)
End Sub

Private Sub picHourColor_Click()
    `Determine selected hour hand color
    mintHourHand = mintHourMouseX
    Form_Paint
End Sub

Private Sub picHourColor_MouseMove(Button As Integer, _
    Shift As Integer, X As Single, Y As Single)
    `Keep track of mouse location
    mintHourMouseX = Int(X)
End Sub

Private Sub picMinuteColor_Click()
    `Determine selected minute hand color
    mintMinuteHand = mintMinuteMouseX
    Form_Paint
End Sub

Private Sub picMinuteColor_MouseMove(Button As Integer, _
    Shift As Integer, X As Single, Y As Single)
    `Keep track of mouse location
    mintMinuteMouseX = Int(X)
End Sub

Private Sub picSecondColor_Click()
    `Determine selected second hand color
    mintSecondHand = mintSecondMouseX
    Form_Paint
End Sub

Private Sub picSecondColor_MouseMove(Button As Integer, _
    Shift As Integer, X As Single, Y As Single)
    `Keep track of mouse location
    mintSecondMouseX = Int(X)
End Sub

ABOUT2.FRM


本書的 第十二章 介紹了一個簡單而且隨處可用的「關於」對話方塊,現在我們要更進一步地介紹App的屬性群以及如何利用這群屬性建立「關於」對話方塊,我們以ABOUT2.FRM做為說明的實例。

ABOUT2.FRM裡所顯示的資料都是由原呼叫程式所設定的,因此,它是一個通用性的表單。要使About2表單顯示出來,只要用frmAbout2.Display方法即可,圖31-13是About2表單由VBClock應用程式呼叫的情形。

到底About2表單上所顯示的資料是如何被設定的呢?祕訣就是在設計階段設定APP物件的屬性群。請在「專案」功能表中選擇「專案屬性」叫出「專案屬性」對話方塊,然後點選「製成」頁籤,如圖31-14。


 

 圖31-13 About2表單執行的情形


 

 圖31-14 「專案屬性」對話方塊中設定App物件的屬性群

「專案屬性」對話方塊可以設定許多App物件的屬性。在「製成」頁籤下的版本編號、名稱以及所有和「版本資訊」相關的屬性,不僅可以在對話方塊中設定,也可以在程式中以App物件加以引用及設定。

About2表單動用了四個App物件的屬性以設定四個標籤控制項的Caption屬性。以下就是設定四個標籤控制項Caption屬性的程式碼,你可以在表單的Load事件程序中找到它們:

`Set labels using App properties
lblHeading.Caption = App.Title
lblApplication.Caption = App.ProductName
lblVB.Caption = App.Comments
lblCopyright.Caption = App.LegalCopyright

圖31-15所顯示的是設計中的About2表單,你可以自行修改目前的設計,例如,加上版本的資訊到對話方塊中。


 

 圖31-15 設計中的About2表單

請按照以下這張表和後面的程式建立About2表單。

ABOUT2.FRM物件與屬性設定
編號 *  屬性
 Form  Name

Caption

BorderStyle

frmAbout2

About

3 - Fixed Dialog

 Label 

1

Name

Alignment

Font

lblHeading

2 - Center

MS Sans Serif, Bold, 12

 Label 

2

Name

Alignment

Font

ForeColor

lblApplication

2 - Center

MS Sans Serif, Regular, 18

&H00008000&

 Label 

3

Name

Alignment

lblVB

2 - Center

 Label 

4

Name

Alignment

lblCopyright

2 - Center

 Line 

5

Name linSeparator
 CommandButton 

6

Name

Caption

cmdOK

OK

*"編號"欄中的號碼用來標示圖31-16中表單上物件的位置。

ABOUT2.FRM原始程式碼
 

Option Explicit

Private Sub cmdOK_Click()
    Unload Me

End Sub

Private Sub Form_Load()
    `Center form
    Left = (Screen.Width - Width) \ 2
    Top = (Screen.Height - Height) \ 2
    `Set labels using App properties
    lblHeading.Caption = App.Title
    lblApplication.Caption = App.ProductName
    lblVB.Caption = App.Comments
    lblCopyright.Caption = App.LegalCopyright
End Sub

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

NISTTime應用程式
 

NISTTime應用程式可以自動撥接美國科羅拉多州布德爾市(Boulder,Colorado)的國家標準及科技研究中心(National Institute of Standards and Technology,NIST),利用這個應用程式,你可以將系統的時間校正到和標準時間之間的誤差少於1秒。儘管透過連線的方式來對時,仍然會產生一些誤差(受數據機速度、電腦運作效率等因素影響),但以NISTTime校正系統時間其準確度仍遠比人工對時方式精準。


注意:

美國地區以外的使用者必須以直撥國際長途電話的方式才能使用這項服務。


NISTTime的運作方式
 

NISTTime應用程式用MSComm控制項來管理數據機與電話線路連接的工作,傳輸速率(Baud Rate)、電話號碼及其他數據機的參數,都能透過MSComm控制項方便地加以設定,你可以在表單的Load事件程序中看到相關的程式碼。NIST原本把這項服務可接受的傳輸速率限定在300 baud和1200 baud之間,但是現在NIST已經裝設了新的數據機,並且以該數據機的傳輸速率作為此項服務的速率限制,因此,範例程式中的14400 baud應該可以運作。如果在你的電腦上使用14400 baud會收到雜訊,那麼請把傳輸速率降下來。

當線路連接成功之後,MIST的時間服務系統會先送出一段資料標頭訊息(Header Message),然後每間隔一秒送出一段時間資料。每段時間資料結束的地方都會有一個星號(*),做為目前確切時刻的記號。另一方面,NISTTime應用程式會把讀取到的資料存放在一個字串變數中,檢視是否收到了兩個星號;一旦第二個星號收到後,應用程式便會擷取兩個星號之間的時間資料,然後根據這個資料來更新系統的時間。

NISTTime應用程式收到的資料包含了其他許多有用的資訊,如Julian日期,萬國同步時間的年月日時間資料,甚至當月份的潤秒秒數,但NISTTime應用程式並不需要用到這些資料。


注意:

你可以用Windows 95 「電話撥號員」撥接NIST的時間廣播服務系統,一旦連線成功後,打入一個問號,你會收到好幾頁有關於這項服務詳細資料。


NISTTime表單上有兩個計時器控制項:一個計時器控制項的Interval屬性設定為1微秒以處理傳來的資料;另一個則被設定為一分鐘以後驅動Timer事件。在正常的情況下,NISTTime應用程式從撥接、設定系統時間到中斷通訊所花費的時間只要幾秒鐘而已,然而,如果整個處理過程中有任何錯誤發生,這個一分鐘的計時器會中斷掉連接的線路。

當NISTTime應用程式執行時,會有一些簡短的訊息告訴使用者目前處理的狀態,等到系統時間設定完成之後,應用程式便會自動地結束。圖31-16到31-18所顯示的是正常的處理程序。


 

 圖31-16 NISTTime應用程式正在撥接NIST的服務系統


 

 圖31-17 NISTTime應用程式正在設定系統時間


 

 圖31-18 NISTTime應用程式正在掛斷電話線路

NISTTIME.FRM
 

NISTTime表單是一張尺寸不大的表單,它包含了兩個計時器控制項,一個MSComm控制項,以及用來顯示處理進度的標籤控制項。圖31-19所顯示的是設計階段中的NISTTime表單。


 

 圖31-19 設計階段中的NISTTime表單

請按照以下這張表以及後面的程式碼來建立NISTTime表單。

NISTTIME.FRM物件與屬性設定
屬性
 Form 
Name

Caption

BorderStyle

Icon

frmNISTTime

NITimeSet

3FixedDialog

CLOCK01.ICO

 Timer 
Name

Interval

Enabled

tmrGetBytes

1

False

 Timer 
Name

Interval

Enabled

tmrWatchDog

60000

False

 MSComm 
Name

Handshaking

comControl

2 - comRTS

 CommandButton 
Name

Caption

cmdCancel

&Cancel

 Label 
Name

Alignment

lblInformation

2 - Center

NISTTIME.FRM原始程式碼
 

Option Explicit

Const PORT = 1
Const TELEPHONE = "1-303-494-4774"
Const SETTINGS = "14400,N,8,1"
Const BUFSIZ = 3000

Dim intNistNdx As Integer
Dim strNistBuf As String * BUFSIZ
Dim strTelephone As String
Dim strSettings As String
Dim intPort As Integer
Dim strA As String
Dim intP As Integer

Private Sub cmdCancel_Click()

    Unload Me
End Sub

Private Sub Form_Load()
    `Locate form near upper-left corner
    Me.Left = (Screen.Width - Me.Width) * 0.1
    Me.Top = (Screen.Height - Me.Height) * 0.1
    `Display first informational message

    lblInformation.Caption = _
        "Dialing National Institute of Standards " & _
        "and Technology Telephone Time Service"
    `Show form and first message
    Show
    `Load port and telephone number from text file
    strTelephone = TELEPHONE
    intPort = PORT
    strSettings = SETTINGS
    Open App.Path & "\Nisttime.txt" For Binary As #1
    If LOF(1) = 0 Then
        Close #1
        `Create file the first time
        Open App.Path & "\Nisttime.txt" For Output As #1
        Print #1, "PORT" & Str(intPort)
        Print #1, "TELEPHONE " & strTelephone
        Print #1, "SETTINGS " & strSettings
    End If
    Close #1
    Open App.Path & "\Nisttime.txt" For Input As #1
    Do Until EOF(1)
        Line Input #1, strA
        strA = UCase$(strA)
        intP = InStr(strA, "PORT")
        If intP Then intPort = Val(Mid(strA, intP + 4))

        intP = InStr(strA, "TELEPHONE")
        If intP Then strTelephone = Mid(strA, intP + 9)
        intP = InStr(strA, "SETTINGS")
        If intP Then strSettings = Mid(strA, intP + 8)
    Loop
    Close #1
    `Set up MSComm control parameters
    comControl.CommPort = intPort
    comControl.SETTINGS = strSettings
    `Set to read entire buffer
    comControl.InputLen = 0
    comControl.PortOpen = True
    `Send command to dial NIST
    comControl.Output = "ATDT" + strTelephone + vbCr
    `Activate timers
    tmrGetBytes.Enabled = True
    tmrWatchDog.Enabled = True
    `Enable Cancel button

    cmdCancel.Enabled = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
    `This usually hangs up phone
    comControl.DTREnable = False
    `The following also hangs up phone
    Pause 1500
    `Update message for user
    lblInformation.Caption = "Hanging up"
    Refresh
    `Send commands to control modem
    comControl.Output = "+++"
    Pause 1500
    comControl.Output = "ATH0" + vbCrLf

    `Close down communications
    comControl.PortOpen = False
End Sub

Private Sub Pause(dblMillisec As Double)
    Dim sngEndOfPause As Single
    `Determine end time of delay
    sngEndOfPause = Timer + dblMillisec / 1000
    `Loop away time
    Do
    Loop While Timer < sngEndOfPause
End Sub

Private Sub SetTime(strA As String)
    Dim intHo As Integer
    Dim intMi As Integer
    Dim intSe As Integer
    Dim dblTimeNow As Double
    `Extract current hour from system
    intHo = Hour(Now)
    `Extract minute and second from NIST string
    intMi = Val(Mid(strA, 22, 2))
    intSe = Val(Mid(strA, 25, 2))
    `Construct new time
    dblTimeNow = TimeSerial(intHo, intMi, intSe)
    `Set system clock
    Time = Format(dblTimeNow, "hh:mm:ss")
End Sub

Private Sub tmrGetBytes_Timer()
    Static blnConnect As Boolean
    Dim strTmp As String
    Dim intBytes As Integer
    Dim intP1 As Integer

    Dim intP2 As Integer
    `Check for incoming bytes
    If comControl.InBufferCount = 0 Then
        Exit Sub
    Else
        strTmp = comControl.Input
        intBytes = Len(strTmp)
        If intBytes + intNistNdx >= BUFSIZ Then
            lblInformation.Caption = "Hanging up"
            tmrGetBytes.Enabled = False
            tmrWatchDog.Enabled = False
            Unload Me
        Else
            Mid(strNistBuf, intNistNdx + 1, intBytes) = strTmp
            intNistNdx = intNistNdx + intBytes
        End If
    End If
    `Check for sign that we've connected
    If blnConnect = False Then
        If InStr(strNistBuf, "*" & vbCrLf) Then
            lblInformation.Caption = "Connected. Setting clock"
            blnConnect = True
        End If
    Else
        `Check for time marks
        intP1 = InStr(strNistBuf, "*")
        intP2 = InStr(intP1 + 1, strNistBuf, "*")
        `Time received if two time marks found
        If intP2 > intP1 Then
            SetTime Mid(strNistBuf, intP1, intP2 - intP1 + 1)
            Unload Me
        End If
    End If
End Sub

Private Sub tmrWatchDog_Timer()
    `Activate safety timeout if no connection
    Beep
    Unload Me
End Sub