26. 專案發展

本章要介紹一些有助於整體專案發展的小技巧。在第一節中,我們要告訴你一個從螢幕上抓圖的技巧,運用這個技巧,你可以抓下執行中應用程式的圖形,把這些圖形整合到說明檔中。第二節和第三節則介紹如何使用資源(Resource File)以及字串資料庫(String Database)進行應用程式國際化的工作。這些技巧可以幫助你發展出國際通用的軟體。

如何抓取執行中的表單然後存成點陣圖檔?
 

這裡有一個非常簡單的小技巧可以讓你直接抓下螢幕上的畫面:按下鍵盤上的Print Screen鍵,從螢幕上被抓下的圖形就立刻會被複製到Windows的剪貼簿中。只要在Windows執行狀態下,你隨時都可以使用這個技巧。如果你只想抓取目前作用中的視窗,請按Alt-Print Screen。本書中大部分表單的圖形都是以Alt-Print Screen抓取的。

把圖形貼到小畫家應用程式中
 

將螢幕上的畫面複製到剪貼簿中之後,只要打開Windows 95的小畫家,就可以把圖形貼入到小畫家裡的空白工作區,如圖26-1,然後就可以開始隨心所欲地編修抓來的圖形。

「另存新檔」與「複製到」
 

如果要把抓下來的圖形存檔,請在「小畫家」的「檔案」功能表中選取「另存新檔」,把檔案類型設定為你所需的點陣圖類型,然後存檔。如果你只要儲存圖形中的某一部分,請先選取欲儲存的部分,然後在「編輯」功能表中選取「複製到」,「小畫家」會要求你輸入檔名,檔名輸入完畢後即可存檔。圖26-2所顯示的是從整個圖形裡選取部分圖形的情形,而圖26-3顯示的是經過「複製到」程序後所產生的新圖形檔的內容。


 

 圖26-1 把抓來的圖形貼入到小畫家裡的空白工作區


 

 圖26-2 從整張圖形中被部分選取的圖形區域(虛線方格中的部分)


 

 圖26-3 以小畫家「編輯」-「複製到」產生的新圖形檔

如何使用資源檔?
 

Visual Basic之所以加入了資源檔的功能是因為資源檔可以協助Visual Basic的程式設計師進行軟體國際化的工作。在Visual Basic還沒提供資源檔之前,如果想用另一種語言來呈現你的應用程式,你必須把每個控制項的標題(Caption屬性)、每個標籤以及其他的字串都加以修改(我們所提到的語言是人"說"的語言而非電腦語言)。現在,你可以把所有在你應用程式中的字串獨立出來,放到資源檔中加以修改,這使得修改成第二種語言的工作輕鬆了許多。

資源檔另一個十分有用的特性是:資源檔允許你直接引用專案中所需的點陣圖檔和圖像檔(Icon File),從資源檔中載入圖形到專案中會比從外部檔案中載入圖形的速度還快。

每個Visual Basic應用程式只能使用一個資源檔,但你可以在資源檔中存放很多字串、圖形、圖像、音效檔甚至視訊檔。

建立資源檔
 

Resource Editor增益功能提供了一個圖形使用者界面,方便使用者產生資源檔。使用Resource Editor之前,請在Visual Basic的「增益集」功能表中載入Resource Editor,然後才能在「增益集」功能中啟動它,如圖26-4。

儲存資源檔時,Resource Editor會自動把資源檔(RES檔)加到專案中。這裡有兩點要提醒你:

  1. 不要使用第一個資源項目(代號1),因為Visual Basic將這個號碼保留了下來,以供應用程式使用內部儲存的圖像。
  2. RES檔是二進位格式。如果要用文字編輯器產生一個資源檔,你要先產生一個RC檔,然後用RC.EXE編譯這個檔案。詳細的步驟請看Visual Studio CD-ROM COMMON\TOOLS\VB\RESOUCE目錄裡的RESOUCE.TXT檔。


 

 圖26-4 Visual Basic的Resource Editor增益功能

在應用程式中使用資源檔
 

圖26-4所顯示的是Moon.VBP應用程式的資源檔,我們以這個範例應用程式介紹如何載入字串和二進位資料檔(Moon圖像)。

現在請建立一個標準執行檔,在表單中加入下列的控制項:文字方塊控制項txtDate,標籤控制項lbl String,圖片方塊控制項imgMoon,以及指令按鈕控制項cmdPhase。圖26-5是發展中的表單。接下來,加入編譯後的資源檔到專案中:從「專案」功能表中選擇「新增檔案」,然後選擇MOON32.RES。

最後,加入以下這段程式:

Option Explicit

Private Sub cmdPhase_Click()
    Dim Phase, ResNum
    `Calculate phase
    Phase = CDbl(CVDate(txtDate.Text))
    Phase = Phase / 29.530589 + 0.9291176
    Phase = Phase - Int(Phase)
    `Calculate resource number
    ResNum = 2 + Int(Phase * 8 + 0.5)
    If ResNum = 10 Then ResNum = 2
    `Load bitmap and string
    imgMoon.Picture = LoadResPicture(ResNum, vbResIcon)
    lblString.Caption = LoadResString(ResNum)
End Sub

Private Sub Form_Load()
    txtDate.Text = Date$
    cmdPhase_Click
End Sub

這個應用程式根據使用者輸入的日期來顯示相對的月球圓缺圖,程式中大部分的程式碼在計算使用者輸入的日期是屬於月球圓缺週期中的哪一個階段,一旦階段計算出來之後,程式會將相對的圖形和描述的文字從資源檔中載入;載入資源的動作由函式LoadResPicture和LoadResString完成。


 

 圖26-5 發展中的Moon.VBP應用程式主表單

圖26-6所顯示的是執行中的應用程式,輸入的日期是1998年7月4日。


 

 圖26-6 執行中的Moon.VBP應用程式

使用資源檔的時機
 

前面提到過Microsoft之所以把資源檔功能加入到Visual Basic中,是為了使應用程式國際化的工作能變得更簡單。在Form_Load事件程序中,你可以很容易地從資源檔中載入字串,設定各種控制項如功能表、圖片方塊、文字方塊等許許多多項目的Caption屬性或Text屬性,這些屬性的內容都可以在執行時期加以修改以反映出使用者所說寫的語言。因此,使用資源檔最大的好處是:本來為了順應使用外國語言所必須做的程式修改工作,現在變成了只要修改外在的ASCII資源檔,簡化了軟體國際化的工作。你可以把資源檔交給一位只懂編輯軟體的資深翻譯人員翻成外國語,這位翻譯人員不需要懂Visual Basic的程式設計。

除了簡化軟體國際化工作的優點之外,資源檔還提供了許多值得一提的好處:第一,應用軟體的執行速度增快了──從資源檔中載入圖形的速度比從外部檔案中載入圖形的速度還快。第二,應用程式變得更容易處理多個圖形。在早期的Visual Basic裡,處理多個圖形最通用的方法是使用多個圖片方塊控制項,在顯示這些圖形時,一次使用一個圖片方塊。從Visual Basic4以後,你已經可以把多個圖形放在資源檔中,顯示時只需要把圖形載入一個圖片方塊控制項即可,避免了處理多個圖片方塊控制項的複雜性,也省下了處理時間。

如何利用字串資料庫進行軟體國際化的工作?
 

你可以使用資料庫來儲存要被翻譯成另一種語言的字串,然後在需要使用這些字串時才從資料庫中讀出,這種方法讓你可以用Visual Basic或Access建立一些簡化翻譯程序的工具。一般而言,資源檔比資料庫較為簡單而且更易於程式化,但如果軟體國際化是最主要的重點,那麼使用資料庫是比較恰當的。

字串資料的結構可以很簡單,在以下這個範例中,如圖26-7,一筆資料錄即包含了表單上所有控制項的各種語言版本的字串。從圖26-7中你可以看到使用者有三種語言可供選擇。


 

 圖26-7 選擇一種語言再按下Show按鈕

資料庫中的資料錄以控制項的名稱作為索引,因此,我們可以在一個For Each迴圈中以控制項的名稱取得該控制項的字串資料。為了判別應該把字串資料指定給控制項的Caption屬性還是Text屬性,我們用一個Select Case陳述式來檢查控制項的種類。控制項的種類可藉由TypeName函式取得。

本範例詳細的程式碼如下:

Option Explicit

Private Sub cmdShow_Click()
    `Translate the test form into the
    `selected language
    If optLanguage(0) Then
        ShowLocal frm1, "English"
    ElseIf optLanguage(1) Then
        ShowLocal frm1, "French"
    Else
        ShowLocal frm1, "Italian"
    End If
End Sub
`Translate the strings on the form
Sub ShowLocal(frmLocal As Object, Language As String)
    Dim wrkDatabase As Workspace
    Dim dbStrings As Database
    Dim recStrings As Recordset
    Dim vntString As Variant
    Dim intCount As Integer
    Dim cntIndex As Control
    `Create workspace, open database, and get recordset
    Set wrkDatabase = CreateWorkspace("", "admin", "", dbUseJet)
    Set dbStrings = wrkDatabase.OpenDatabase("strings.mdb")
    Set recStrings = dbStrings.OpenRecordset("Strings")
    `Use names of controls as index in recordset
    recStrings.Index = "ControlName"
    `Internationalize each control name
    For Each cntIndex In frmLocal.Controls
        recStrings.Seek "=", cntIndex.Name
        Select Case TypeName(cntIndex)
            `Change Text property for text boxes
            Case "TextBox"
                cntIndex.Text = recStrings.Fields(Language)
            `Change captions on others
            Case "Label", "OptionButton", "CheckBox", "Frame", _
                "CommandButton"
                cntIndex.Caption = recStrings.Fields(Language)
            `Change List property for list boxes (record contains
            `an array)
            Case "ListBox", "ComboBox"
                vntString = MakeArray(recStrings.Fields(Language))
                For intCount = 0 To UBound(vntString)
                    cntIndex.AddItem vntString(intCount)
                Next intCount
            `Ignore pictures, timers, scrollbars, and so on
            Case Else
        End Select
    Next cntIndex
    `Internationalize form name
    recStrings.Seek "=", frmLocal.Name
    frmLocal.Caption = recStrings.Fields(Language)
    `Close recordset and database
    recStrings.Close
    dbStrings.Close
    `Show the form
    frmLocal.Show
End Sub

`Utility function to convert a semicolon-delineated list
`to an array
Function MakeArray(strSource As String) As Variant
    Dim vntTemp()
    Dim intCount As Integer, intPos As Integer
    Do
        ReDim Preserve vntTemp(intCount)
        intPos = InStr(strSource, ";")
        If intPos Then
            vntTemp(intCount) = Left(strSource, intPos - 1)
            strSource = Right(strSource, Len(strSource) - _
                (intPos + 1))
            intCount = intCount + 1
        Else
            vntTemp(intCount) = strSource
            Exit Do
        End If
    Loop
    MakeArray = vntTemp
End Function

在起始表單上有一個Edit按鈕,按下這個按鈕後,你可以修改資料庫中的資料錄,如圖26-8。我們用Microsoft Access建立STRING.MDB資料庫,然後以這個資料庫透過「資料表單精靈」建立圖26-8中的資料表單。

你可以用Debug旗標來控制Edit指令按鈕出現的時機,防止使用者修改資料庫。只要把Edit指令按鈕的Visible屬性設為False,然後加入以下的程式碼,然後你就可以靠設定旗標來決定何時才讓資料表單出現:


 

 圖26-8 按下Edit按鈕以修改資料庫
#Const DebugBuild = 1

#If DebugBuild Then
    Private Sub Form_Load()
        `Display Edit button only on debug builds
        cmdEdit.Visible = True
    End Sub
    Private Sub cmdEdit_Click()
        `Display data entry form for the database
        frmStrings.Show
    End Sub
#End If

採取這種設計的方式,你的應用程式就有了一個內建的資料庫工具,可以用來維護各種語言版本的字串資料。


參考資料:

請參閱Visual Basic線上手冊中的Designing International Software and General Considerations When Writing International Code主題。