31.日期與時間
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