2014年6月30日 星期一

FireMonkey 桌面應用程式最小化

問題 - 視窗縮小時不會整個應用程式縮小

在製作 Delphi 或 VB 程式時, 常常會使用多個 Form, 在每個 Form 的畫面當中, 會賦予不同的功能, 但每個 Form 都會被當成獨立的畫面, 也就是 MainForm.

我們對 MainForm 的預期, 就是在按下右上方的縮小鍵時, 整個應用程式縮小到工作列上, 按了工作列上的按鈕時, 整個程式畫面還原回來原來的狀態.

但從過去 VCL 的時期到現在的 FireMonkey, 都並沒有這麼容易, 多個 Form 的時候, 只要不是按下 MainForm 的縮小按鈕, 視窗都會縮在桌面左下角。

視窗沒有縮到工作列上, 而是成為縮小視窗
以往這種 Form 的特性, 是為了製作 MDI Application, 也就是一個大的 Form 當中有多個文件會被開啟, 就像 Word, Powerpoint 那樣, 可以開啟多個文件, 讓每個文件有獨立的小視窗用來檢視文件。

以往在 VCL 架構的解決方法

在 VCL 架構的應用程式中, 解決這個問題的方法, 是在任何一個 Form 裡面建立一個 onMessage 的事件處理常式, 並在 FormCreate 事件中把它指派給 Application.OnMessage 事件:

procedure TForm1.HandleMessage(var Msg: TMsg; var Handled: Boolean); 
begin
   if (Msg.Message=WM_SYSCOMMAND) and (Msg.wParam=SC_MINIMIZE) then
     Application.Minimize;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   Application.onMessage := self.HandleMessage;
end;

當我們發現 SC_MiniMize 事件發生, 就直接執行 Application.Minimize, 整個應用程式就會縮到工作列上了, VCL 上面如此, FireMonkey 卻不然...... 時至今日, 網路上的各個論壇還在尋找著跟 VCL 架構當中的 Application.onMessage 對應的事件...... 然而, 我也找不到, 但是在 FireMonkey 當中, 有很多玩法跟 VCL 不相同, 花了一陣子搜尋 XE5 裡面的 RTL 原始碼, 找到了這個玩法, 有效, 簡易, 跟大家分享。

範例-製作兩個 Form 互相切換

先建立一個 FireMonkey 應用程式, 包含兩個 Form, Form1 跟 Form2, Form1 是 預設的MainForm, Form2 則是一般的 Form.

Form1, 放一個 button 切換到 Form2
Form2, 放一個 button 切換到 Form1













Form1 上面的 Button1Click 內容如下
procedure TForm1.Button1Click(Sender: TObject);
begin
   form2.Show;

   self.Hide;
end;

Form2 上面的 Button1Click 內容如下
procedure TForm2.Button1Click(Sender: TObject);
begin
   form1.Show;

   self.Hide;
end;

這是最常見的寫法, 然而切換到 Form2 的時候, 按下視窗上的縮小按鈕, 就會跟本文最上方的圖片所顯示的一樣:

這時候, 只要在 Form1 的 Button1Click 加上一行:
Application.MainForm := form2;

就可以把 Application.MainForm 從 form1 切換成 form2, 此時再按 form2 的縮小按鈕, 就不會再出現縮小的視窗了耶, 超神奇, 兩個 click 事件處理常式如下
修改後, Form1 上面的 Button1Click 內容如下:
procedure TForm1.Button1Click(Sender: TObject);
begin
   form2.Show;
   Application.MainForm := form2;
   self.Hide;
end;

修改後, Form2 上面的 Button1Click 內容如下
procedure TForm2.Button1Click(Sender: TObject);
begin
   form1.Show;
   Application.MainForm := form1;
   self.Hide;
end;

結語

FireMonkey 上面許多用法都很神奇, 由於所有元件都有 Container 的特性, 所以可以直接收納其他元件, Application.MainForm 也不是唯讀屬性, 所以可以在切換 form 的時候順帶把 Application.MainForm 做重新指派, 雖然很難想到這個用法, 不過它確確實實的有效, 程式碼也簡潔, 所以推薦給大家使用.

範例程式連結

2014年6月28日 星期六

FireMonkey 分享 - TImageButton 的製作

緣起

在以 VCL 撰寫桌面應用程式的時候, 常會遇到一個狀況, 就是需要讓某個按鈕用美術人員設計的圖片當成底圖, 而且該按鈕還要有四種狀態:

  1. Normal (一般狀態時)
  2. HighLighted (滑鼠游標進入按鍵區域時)
  3. Pressed (滑鼠按下時)
  4. Disabled (設定為不能點選時)
這樣的按鈕, 在過去 VCL 的時代, 有 TImageButton, TBitmapButton 等各種第三方元件可以使用, Delphi 內部也有一兩個內建的元件 (TBitButton? 不知道我有沒有記錯) 可以讓我們把圖片放上來, 然後設定一張圖片 (或多張) 給該按鈕不同屬性作為顯示圖片.

VCL 時代, 由於對圖片格式的支援, 在每一版的 Delphi 都不太一樣, Delphi 6 好像預設只有支援 Bitmap, 如果要使用 Jpeg 格式的圖片, 就得自己在 use 的區塊輸入 Jpeg 這個 unit 的名字.

如果要使用 Png 的話, 在 2005 年前後則有了 TPngObject 這個第三方工具可以使用, 但還是必須由程式人員自己去找到、引入才行.

所以我在 2005 年也自己寫了 TPngImageButton 這個 VCL 元件, 在 TButton 元件的屬性中加入了四種狀態的圖片, 可以在設計階段 (Design Time)把 Png格式的圖片設定好, 並存在 dfm 檔案裡面.

但當時只想著直接用圖片來解決一切, 並沒有想到有一天在處理多國語系的時候, 裡面還要多放個 TLabel, 好讓自己處理多國語系的介面文字顯示時能夠簡單一點, 這一切, FireMonkey 都已經處理好了.

元件容器的功能 (Container feature)

在 VCL 架構的應用程式裡面, 如果我們要讓兩個 VCL 元件結合在一起, 並且在 Design Time 的 Form 上面, 則其中一個必須具備包容其他 VCL 元件的功能,在 VCL 的元件當中, 並非全部都具備此一特性, 僅有以下幾個元件有元件容器的特性:

  • TPanel
  • TPageController (其中的各個分頁)
  • TGroupBox

很明顯的,TButton 並不屬於其中一員,所以,要在TButton裡面放上一張圖片,並在Design Time進行各項屬性的調整、設定,在 VCL 版本的 TButton 是無法達成的,真的需要這樣的Button。我們只好新建一個 VCL 元件,繼承 TButton,然後 implement 各項 TButton 的屬性與方法,然後再透過 dpk 與 bpl 把這個新的 VCL 元件安裝到 VCL 元件盤 (component plate), 這樣就能達成 VCL 的 TImageButton 了,但這篇分享文章的主題不在 VCL,所以要跟各位說聲抱歉,如果有朋友需要我的TPngButton的話,下次有機會再把該元件分享給大家。

FireMonkey 的所有視覺化元件元件都是 Container

元件容器的功能,在容納其他的元件,VCL 並非所有元件都具備此功能,但在FireMonkey 就完全改變了這一點,FireMonkey 的所有視覺化元件都具備 container 的功能。

換句話說,我們可以從Delphi 的IDE環境左上角的 Structure 畫面,任意把任一元件拖拉放到其他元件上,立刻就能把多個元件結合起來,成為一個新的元件,不需安裝,也不像 bpl 得在功能有所變動的時候就得重新編譯、安裝,當然,如果您想要把這個複合的元件建立成一個新的元件放到元件盤上面去,也是做得到的。

步驟一:拉一個 TButton 到 form 上面

從元件盤上找到 TButton (如果您對 Delphi 還不熟悉,請從 Delphi 畫面右下角找到元件盤,在搜尋框裏輸入 button, 就會顯示很多名稱當中有 button 這個字詞的元件, 第一個符合條件的就是 TButton),找到以後,把它拖拉到您的 form 上面任意的地方。

從元件盤進行關鍵字搜尋

不麻煩的話,幫您的元件取個容易看出其用途的名字吧,一堆 Button1,Button2的按鍵很容易讓您的程式失去可讀性,也增加了維護的難度。

步驟二:拉兩個 TImage 到 form 上面

從元件盤裏拉兩個 TImage 元件到您的 form 上面來。

請注意,名稱裡有 Image 這個字詞的元件還不少,我們只要 TImage,不要 TImageControl 哦,TImage 在 FireMonkey 的元件盤裡,被歸類在 Shapes 裡面,在 Common 跟 Advance 分頁裡面是找不到的。

從元件盤上面搜尋 TImage 元件

設定圖片
TImage元件拉好了以後,請從左下角的屬性檢視視窗(Object Inspector) 設定要顯示的圖片:
幫 Image 元件設定要顯示的圖片
點擊屬性檢視視窗裡面的 MultiResPicture 欄位,裡面會出現一個小按鍵,點擊它,就可以選擇要放進 TImage的圖片檔案了。

選擇要放在 Image 裡面的圖片檔案
FireMonkey 所支援的圖片格式很多,我們最常用的是 Jpeg 跟 Png 這兩種格式,請自行審酌您要用哪一種。

設定 Tag
Image 拉進來之後,要不要給他們命名?不重要,因為在這個範例裡面,我們用來判別圖片的依據是tag,而不是 Image 的name。

請把要設定為一般狀態的 Image 元件的 tag 欄位填為 1,要設定為被點擊 (Pressed) 狀態的 Image元件的 tag 欄位填為 2。tag 欄位一樣可以從左下角的屬性檢視視窗找到,所有元件的 tag 預設值都是 0. (請參照上圖的下半部)

Tag 是物件導向程式設計當中相當重要的一個概念,當同一個 Class 在 RunTime 被建立出多個實體 (Instance),這些個實體是無法事先命名的,此時,Tag 就可以用來分辨到底是哪一個實體被點擊或被觸發了事件,在這個案例裡面,也會得到練習。

圖片沒有填滿整個 Button?
如果您的圖片沒有填滿整個Button的區域,此時上下左右一定有些空白區塊。請把 Image 設定成拉伸填滿即可,這個屬性是ImageWrapMode,在屬性檢視視窗的最後一個,請將它設定為 iwStretch,預設是 iwFit,只會上下拉伸或左右拉伸。
請選擇 iwStretch, 讓圖片能變形填滿整個 Image 的區塊

步驟三:把 TImage 拉進 TButton 裡面

聽起來很玄吧? 要怎麼把一個元件拉到另一個元件裡面?

從 Delphi 2005 以後,Delphi 的 IDE畫面就是現在這個模樣,左上角的視窗叫做 structure,也就是結構。我們可以從這個結構視窗裡面任意拉動元件,在這裡拉動元件,會改變元件的從屬關係,我們在結構視窗裡面把剛剛的 Image1 拖放到剛剛的 Button 元件上,看看發生了什麼事?
結構視窗拖拉元件的畫面 (拖拉中)
Delphi 當掉了,沒有回應!!! 這是 Delphi 做這個動作時還蠻常發生的事,在結構視窗裡拖拉元件前請先記得存檔啊!

正常情況下,Image1 會變成 Button 的子元件,如同畫面上的樹狀結構所顯示的。接著請把 Image2 也拉進去 Button 裡面。

這時候,您可以看到Button的小小範圍裡面出現了兩張圖,這兩張圖還重疊呢。
先別急,就是要它們重疊。先把兩張圖的 align 屬性 都設定成 alClient,也就是讓它們都佔滿整個Button 的顯示範圍。

align 屬性,請從屬性檢視視窗中尋找,應該會是 Image 元件的第一個屬性,您點擊其右半部,就會出現下拉式選單讓您選擇,找到 alClient,點擊它即可完成設定。

把 Image 的 align 屬性設定成 alClient

迫不及待想執行看看嗎?就執行吧,這時候的Button只會顯示最上面那張圖片,會是哪一張?我也不知道,端看您拖拉Image到Button裡面的時候,哪一張圖片在比較前方,這無法用直覺操作來設定,需要透過滑鼠右鍵的功能選單來設定。別急,距離完成還有幾個步驟。

先從結構視窗找到 Image2,把它的 Visible 屬性設定成 false,此時在IDE當中並不會有任何改變,這些屬性的變化只會在RunTime 反映在介面上。您可以按 F9 執行看看。

到了這裡,我們的Button上面有圖片,但對於點擊還沒有畫面上的反應。

步驟四:設定 TButton 的 OnMouseDown 跟 OnMouseUp 這兩個 eventHandler

接著,我們要來寫程式了!

請點擊 Button,並從元件檢視視窗中選擇 events 分頁,這裡會列出所有可以直接處理的事件,例如 onClick,onDblClick 等等。

請找到 onMouseDown 這個事件,目前它在視窗右半部是空的,請雙擊它。這樣就能讓 Delphi IDE 幫我們產生一個 event handler (事件處理常式),畫面也會切回程式碼編輯視窗了。

在 onMouseDown 的時候,我們要讓有按壓效果的圖片顯示,隱藏其他所有的圖片,但位於 Button 內部的圖片最好別每個元件的 name 都寫在程式碼裡面,不然,如果有 100 個按鍵,豈不是要寫 100 個 eventHandler?! 那可要人命了。

所以我用一個迴圈來解決,程式碼如下:
procedure TForm1.btnCheckInMouseDown(Sender: TObject;
   Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = 2);
   end;
end;
 
procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
   selectedIdx: integer;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = selectedIdx);
   end;
end; 
從上面的程式碼,可以看到,eventHandler 的參數是 Sender,型別是 TObject,這是 Delphi 的標準 eventHandler 宣告,在執行時,Sender就會是該事件被觸發的元件,例如我們有個最簡單的 onClick eventHandler,當它被呼叫時,Sender 參數就是被點擊的那個Button,也可以是被點擊的TLabel,或任何對該eventHandler進行了binding 的元件。

因此,雖然 Sender 的型別是TObject,但實際上可以轉型成 TButton,TLabel,當然也可以是 TImage。不過剛剛請您點選的是 Button 的 onMouseDown 事件,所以這裡的 Sender 是一個 TButton,不過也要請您留意,直接在 form 上面點元件的話,還是很可能點到放在 Button 裡面的 Image 哦,要小心,從結構視窗點擊比較準確。

第一行,我就把 Sender 轉型成 clickedButton,透過 as 語法,Sender 會被《當成》TButton,存放在 clickedButton 這個變數名稱中。

在 TFMXObject 裡面,都有個通用的屬性:Children,用來存放該元件所內含、收容的所有子元件,所以我們也能從這裡面找出剛剛拉進去的兩個 Image。

上頭的程式碼裡面,我用了for..in 迴圈,這是Delphi 2009 以後加到 Object Pascal 語言裡面的新語法,可以列舉某個 container 元件或資料結構裡面的所有內容物,進入到 for..in 之後,我們所取得的名為 enumObj 的元件,就是被收納在 clickedButton.Children 裡面的每個元件。

把元件一一找到了,接下來得分辨哪些是圖片,然後把按壓效果的圖片的Visible 屬性設成 true,其他的都設定成 false,這樣就能做出按鍵被壓下去的視覺效果了。

但是要先找出型別是 TImage 的元件,然後才設定其 Visible 屬性?還得多做一次判斷,這時候我們直接檢查 tag 就行了,前面提到過,預設的元件 tag 都是 0,只有我們拉進去的兩張圖片 tag 是 1跟2,所以超容易判斷,tag 等於 2 的元件, Visible 就是 true,其他的都是 false,夠簡單吧?

步驟五:完成一半了!來讓 image 不干擾 Button 的位置拖拉 (Design Time)

完成至此,Button在執行時期 (RunTime) 的效果大致完成了,但有時候介面設計的同仁或美術設計的同仁總是會天外飛來一筆,更改介面設計!

如果只是改張圖片,那算簡單的,如果位置、規則流程全變了,也是常有的事,但目前的Button還沒辦法直接用拖拉的方式調整,您試著拉拉看,現在應該只會拉到圖片,而且因為我們把圖片的align 屬性設定成 alClient 了,所以根本連圖片也拉不動了,如何是好?

在 FireMonkey 的元件當中有一個新的屬性,稱為 Locked,預設值是 false,也就是允許拖拉,我們現在把 Image的 Locked 屬性設定成 true,就可以把 Image 鎖定在現在的位置,可以讓我們在DesignTime 拖拉時,把拖拉的事件直接轉給父元件,這麼一來,就變成在拖拉 Button,而 Image 就變成了一張不會動彈的底圖了,很棒吧?!

設定 HitTest 跟 Locked.

別忘了,兩個 Image 元件的 Locked 屬性都設定為 true,這樣在 DesignTime 的拖拉動作,就可以直接拖拉 Button, 而不會誤拉到 Image 了.
正常狀態
點擊時狀態

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

步驟:讓底圖的點擊事件不干擾到 Button 的點擊事件

HitTest 這個屬性, 也是 FireMonkey 元件特有的, 這個屬性設定成 true 的時候, 該元件就會取得滑鼠點擊的事件, 我們現在不希望底圖有被點擊的機會, 以免干擾 Button 的所有事件, 所以要把兩個 Image HitTest 設定成 false,  這樣就可以把 Image 變成完全不會干擾 Button 事件的底圖了。

步驟:加個 TLabel,以不時之需

有時按鈕上的圖片只能是底圖的呈現, 如果按鈕上有需要顯示文字, 最好用 TLabel 來顯示, 這樣一來如果有多國文字要呈現, 才能用一套圖片, 以及準備好的多國文字直接完成多國語系的需求。

首先, 我們先重複前面幾個步驟:
  • 拉一個 TLabel 到 Form 上面
  • 把 TLabel 拉進 Button 元件裡面
  • 把 TLabel 拉到 Button 裡面適當的位置
  • 設定 TLabel 的 Tag 為 11
  • 修改 Button 的 onMouseDown 跟 onMouseUp
  • 設定 TLabel 的 Locked, HitTest 為 false
以下就是修改完成的 onMouseDown, onMouseUp 這兩個 eventHandler 的程式碼, 請注意, 這次加上了 Tag > 10 以上的元件也都要顯示喔, 至於是否要轉型成 TImage, 這段程式沒有影響, 因為並沒有直接存取到 enumObj 的屬性或方法, 只有對所有元件共通的 Visible 屬性進行修改, 所以不至於會造成 Access Violoation.

如果有要對其他屬性做修改的話, 記得要適當進行轉型後才行.

procedure TForm1.Button1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
begin
   clickedBtn := Sender as TButton;

   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = 2) or (enumObj.Tag > 10);
   end;
end;

procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
   enumObj: TFMXObject;
   clickedBtn: TButton;
   selectedIdx: integer;
begin
   clickedBtn := Sender as TButton;

   if clickedBtn.StaysPressed then selectedIdx := 2
   else selectedIdx := 1;
   for enumObj in clickedBtn.Children do begin
      TImage(enumObj).Visible := (enumObj.Tag = selectedIdx) or (enumObj.Tag > 10);
   end;
end;

完成這幾步以後,Label 就成為了 Button 裡面的一員,我們的 Button 裡面就可以顯示文字了.
 

步驟:完成了,如何在同一個 form 裡面複製這樣的 Button?

只要在結構視窗裡面, 先點選已經做好的 Button, 按下 Ctrl + C, 就像我們在 Windows 的其他任何的編輯器裡面進行複製一樣。

 接著點選我們要放置這個 Button 的元件, 例如在不同的 TabItem 裡面, 或者另一個 TRectangle 裡面, 點選好以後, 我們按下 Ctrl + V, 就像我們在 Windows 的其他任何編輯器裡面進行貼上的動作一樣

Button 就會被貼在該元件上面了, 這時候我們就可以編輯 Label 的文字內容, 拖拉 Button 的位置到適當的地方去, 跟一個原生的 TButton 元件一樣

總結

透過本篇的介紹,和大家分享了如何在 FireMonkey 架構下製作出內含有圖片、Label 的按鍵,在這個過程中,大家也一併了解、認識了幾個 Delphi 的重要概念:

  • FireMonkey 的所有視覺元件都是 Container, 具備包含其他元件的能力
  • FireMonkey 元件的 Locked 與 HitTest 這兩個屬性的特性與用途
  • Delphi 的 for..in 迴圈寫法
  • 元件當中的 Tag 屬性對於 Delphi 元件的重要性
  • Delphi 元件的 as 用法
  • 如何改變 FireMonkey 裡面的 TLabel 字體、大小,以及用RGB色碼改變 TLabel 的顏色

自我練習

本篇的介紹不一定適用於所有情況,有些時候Button 會因為美術人員的設定、企劃人員的規劃而有許多呈現上的變化,這時候大家就需要發揮創意對本篇的概念做一些微調,大致上可能有以下這幾種變化:

Label 的位置不一定正好在 Button 位置的正中間,需要跟畫面上的其他小圖示有相對位置的設定。
加上 MouseOver 的圖片效果時,要怎麼實作?
除了主要的圖片外,還有位置可能變動的小圖示,要怎麼處理?

請發揮您的想像力,想想上述這幾個狀況要如何修改程式或元件設定才能達成需求?

2014年6月26日 星期四

Delphi 浴火重生, 火猴的逆襲已經來臨

楔子

自 FireMonkey 推出以來, 漸漸燃起了從 2000 年以來這十年間, Delphi 程式人員對於 Delphi 的信心。從 2000 年 Anders Hejlsberg 琵琶別抱, 為微軟量身訂做了.NET 平台與 C# 程式語言之後, Delphi 的聲勢江河日下, 再加上 Borland 的策略失準, 以行銷為導向, 失卻了 2000年以前 Delphi 所自豪的穩定性與效能, 曾被稱為 VB Killer (VB 殺手) 的 Delphi, 在 2000-2010這個十年之間, 完全失去了主要程式開發工具的地位。

然而, 在歐洲各地的 Programmer 們並沒有受到 Delphi 崩壞的影響, 在俄羅斯、義大利、法國,Delphi 在程式開發的這個圈圈裡, 仍舊有許許多多的人在努力著, 從 2001 (年份我可能記得不清楚了) 的 Delphi 8, 到之後的 Delphi 2005, 2006, 2007, 2009, 2010, XE, XE2, XE3, XE4, XE5, 到 2014 年的 XE6, 各式各樣功能的第三方 VCL 元件不斷在全世界被創造、散佈,使用在各種場合與應用裡.

Tnt 元件

在 Delphi 2009 發佈之前, 所有 Delphi 的元件與 IDE 並沒有支援 Unicode 的字元, 也因此從 Delphi 5 開始, 就有兩三種內建的工具讓程式開發人員可以製作出 Resource 檔案, 用來儲存 Form 當中的各種多國語言的內容, 然而, Unicode 才是王道, 沒有 Unicode, 多國語系真的是個遙不可及的夢想。

Tnt系列元件, 是提供尚未支援 Unicode 的版本的 Delphi (Delphi 2009 以前的所有版本)對應的 VCL, 從 TLabel, TEdit, 到 BDE 的絕大多數元件, 都有對應的 Unicode 版本元件, 對應上來就是 TtntLabel, TtntEdit 等等。

在提供多國語系的應用程式上, 或者處理跨國企業的資料庫時, 都對程式人員提供了非常多的幫助, 感謝 Tnt 元件的開發者.


Indy 網路元件組

從 1999 年的 WinShoes, 到 2000 年開始的 Indy 7, Indy 8, 一直到目前搭載在 Delphi XE5 的 Indy 10.60, Indy 陪伴著 Delphi 開發人員走過了將近 20 個年頭, 從 Client, Server, Protocol, 各式各樣的功能, 讓開發人員處理網路相關的程式功能更不費力.

目前 Delphi XE5 當中的很多官方元件, 例如 DBX 系列元件的底層, 也都是以 Indy 作為其基礎, 因此如果我們遇到 DBX 系列元件發生了什麼問題, 也就可以從 Delphi 內附的 RTL 或 VCL 程式原始碼來抓出到底是那兒出了問題.


跨平台的努力-FireMonkey

從 Delphi 2009 把 Unicode 功能完完整整, 裡裡外外的融入了 Delphi 的 IDE, 編譯器以後, 我們現在已經可以在程式裡面用中文變數, 甚至各國文字作為變數, 要怎麼處理多國語系, 都不是問題.

但 2009 年的時空, 已經不是 Windows 桌面應用程式的天下, 就連網頁程式也已經是小學生就能提供的東西, 因此, Delphi 的下一版, 除了把 64 bit 編譯器做出來, 讓效能能夠更好, 接著就是朝向跨平台的目標前進.

在 1993-1995年間, JAVA 的概念與程式標準開始被 SUN 提出, 每個新的產品都有其行銷的口號, JAVA 當時的口號就是: Once Compile for All Platforms. (編譯一次, 所有平台都可用)

這個口號非常美好, 但大家可以在後來的桌面應用程式跟網頁程式當中得知, 這個口號又是另一個夢幻泡影. (如露亦如電, 應作如是觀 - 引述自金剛經)

JAVA 的 Once Compile for All Platforms, 是奠基在『所有硬體設備上面都會提供有 JAVA 虛擬機器 (本文後將簡稱為 JAVA VM)』的這個前提假設上, 然而, 並非每個硬體設備都有 JAVA VM, 且當時設計這個概念的人也沒想到 JAVA VM 的效能簡直是一場惡夢.

Delphi XE2 開始, 把這個概念作了一些些修正, 叫做 One Code for Multiple platforms (同樣一份程式碼, 能夠在多種平台上執行), 這個口號聽起來沒有很炫, 但卻實際多了.

從 Delphi XE2 開始, 沿用了 20 多年的 VCL 有了 32/64 bit 兩種版本, 另一組新的視覺元件庫: FireMonkey 也被推出, 透過 FireMonkey 元件組所撰寫的程式 (我們先討論一般桌面程式), 就可以直接編譯成 Win32, Win64, 以及 OSX (Mac 作業系統).

雖然要編譯三次, 在操作上多一些些步驟, 但編譯出來的程式, 在各個平台上都是原生程式碼, 不再透過像 JAVA 那樣高階的虛擬機器, 而能夠讓編譯出來的程式碼直接就是機器碼, 這效能的提升, 比透過直譯器達成的程式, 或者虛擬機器達成的程式, 都高出了不知道多少倍.

且 FireMonkey 所提供的介面, 在設計階段都是 Windows 的外觀, 但實際執行的時候, 在Windows 跟 Mac OSX 上面, 都完完全全是原生平台的程式外觀, 不用程式人員再花額外的功夫去處理佈景或圖片的問題了.

至此, 跨平台的功能頗有小成, 但請回想一下, 2009 年, 已經是 iOS 跟 Android 打的死去活來, Windows Phone 只能撿一些小屑屑, 市佔率幾乎達不到兩位數, 開發人員完全不會去想提供 Windows Phone 平台的應用程式.....

因此接下來的 XE3, XE4, XE5, XE6, Delphi 就花了更多的功夫在這兩個完全引領主流的作業系統上面, 到 XE5 算是有所成, 第一次能夠讓 FireMonkey Mobile 的同一份程式碼在 iOS 跟 Android 系統上面執行.


未來將何去何從?

這個題目太大, 即使問微軟說: 你們的 Visual Studio 接下來要提供什麼功能? 相信微軟的開發人員也會一下就楞住, 我相信 Delphi 跟 C++ Builder 也一樣.

目前 Visual Studio 2012 版, 提供了 C#, VB, VC 三種程式語言, 可以透過 .NET 開發網頁程式、Windows 應用程式, 以及透過一些第三方工具, 讓一部分程式能夠編譯成 iOS 能執行的 app (是透過像 JAVA 那樣的虛擬機器, 類似 Mono).

有了網頁, 桌面應用程式, 手機應用程式, 程式人員對 Delphi 的未來還有什麼期待?

未來是不斷在人們手中開創的, 2005 年的時空下, Windows Mobile 幾乎主宰整個智慧型手機市場, 靠的是 Compact .NET framework, 然而, 誰會知道 2007 年 iPod Touch 即將推出, 2008 年 iPhone 2G 與 App Store的推出會讓 XCode (Objective C) 在兩年內席捲全球? 2009 年 Android 系統跟 Eclipse 讓 JAVA 借屍還魂?

誰又知道 2014 年六月, Apple 會推出語法近似 JAVA 與 C#, 包含有完整物件導向概念, 原生相容於 XCode 所有 Framework 的新程式語言 Swift? (按筆者: 所有程式語言, 請從其概念來理解, 則只需要 Reference 文件就可以很快學會, 大約 1-2周吧)

未來, 永遠是軟體人員心中永遠的期許, 也是傷痛, 這兩個字代表著無限的學習, 永不停止.


對 FireMonkey 的不熟悉

FireMonkey 是一組全新的元件, 大多數一直使用 Delphi 的程式人員, 都有著陌生的感覺, 在台灣, 除了 Embarcadero 原廠的文件與代理商捷康努力的推廣, 請李維大師持續的在這塊領域上耕耘之外, 能夠說自己已經熟悉 FireMonkey 的人恐怕不多.

筆者從 Delphi XE5 使用至今, 剛剛有一些些熟悉, 未來也會把這些相關經驗在網路上分享出來, 期望透過交流與跟網路上的開發人員交互學習, 讓 Delphi 與 FireMonkey 能夠發揮出很好的功能, 讓 Delphi 的程式技巧能夠再次引領業界與學界的風氣, 在未來的某一天, 如果台灣能夠開始對軟體開發稍微正眼看待的時候, 讓 Delphi 與 C++ Builder 能夠成為程式人員前幾名的選擇.

Dennies
2014 年六月.