2011/05/23

又是筆記(DELPHI 5 - CheckListBox)

公司舊有的程式裡有很多使用CheckBox來勾選店別的地方,都是那些可勾選多店別執行的程式。然而隨著分店越開越多,程式也老是增加店別選項。為了一勞永逸,我決定來改寫那些程式,把它們的店別選項用CheckListBox去抓取系統中的店別資料來解決。





  其實本來只是一件很簡單的事,就是把店別和店名都寫進CheckListBox去給User勾選就好,但是我還是很龜毛的打算讓它變得像原本舊有的程式畫面--只有店名。但是Delphi的CheckListBox並不支援一個選項有雙欄位記錄資料,也就是這樣顯示:
    口A1|B1
    口A2|B2
    口A3|B3

它只能這樣顯示:
    口A1
    口A2
    口A3

  有時候在B欄位我們會想要放置一些隱藏的值,只給使用者看到選項,而選中後我們再去依這些隱藏的值去做事。例如在A欄位是歌名,而B欄位則隱藏著那首歌的路徑檔名,當使用者選擇好歌曲後,我們就可以直接取得路徑和檔名來播放。

  上網搜尋了許久,找到了在DELPHI中解決的方法,那就是使用AddObject,參考這兩篇:第一篇delphi中AddObject的使用方法,第二篇listbox怎麼保存兩個字段值,且後一個字段隱藏。這兩篇都是使用AddObject方法,但是作法稍有不同,然而在實值上都是一樣。那就是向記憶體買一個小盒子,將選項其對應的隱藏值存放到裡面,當使用者選擇該選項時,指標就會去這個小盒子找到對應的隱藏值出來。

  經過實驗,rec的方法是可行的,不過我還沒研究出來要怎麼多選項。所以這個筆記只記錄了使用StringList的方法(第一篇)。

  第一步:建立全域StringList變數。實際上這個設定依程式而異,但是我認為通常使用CheckListBox的時機絕對不是在同一個動作(procedure或Function)中就會結束,所以StringList的壽命也必須要和CheckListBox一樣。如果撰寫程式的人員也較喜歡或習慣寫成模組化,使用CheckListBox值的機會也不局限在一個動作中,那麼使用全域應該是必然的。
var SL: TStringList;//宣告SL變數

  第二步:建立/增加CheckListBox選項。模式就是AddObject(選項, TObject(儲存位置))這樣而已,所以如果不使用StringList,要使用其他方式也是可以。
procedure TForm1.Button1Click(Sender: TObject);
var i:Integer;
begin
CheckListBox1.Items.Clear;//先清空
SL:= TStringList.Create;//宣告建立StringList

//不使用迴圈時建立
{SL.Add('B1');//隱藏值
CheckListBox1.Items.AddObject('A1', TObject(SL[0]));//建立
SL.Add('B2');
CheckListBox1.Items.AddObject('A2', TObject(SL[1]));
SL.Add('B3');
CheckListBox1.Items.AddObject('A3', TObject(SL[2])); }

//使用迴圈建立,適合搭配其他程式
for i := 0 to 9 do begin
SL.Add('B'+inttostr(i));//隱藏值
CheckListBox1.Items.AddObject('A'+inttostr(i), TObject(SL[i]));
Next ;
end;

end;


  第三步,有借有還,所以建立就要有刪除,否則這個程式會一直佔用記憶體,尤其是量相當龐大的時候。至於該在何時釋放,就要看程式使用CheckListBox的程度。如果取得隱藏值之後就不需再使用,那麼取得值後就可以釋放,如果在關閉程式時都還有可能使用,那麼最好就是在FromClose時執行。
FreeAndNil(SL);//釋放記譯體

  第四步,取得隱藏值。在原本參考的網站中,有提到儲存文字內容,在取值的時後變成亂碼的情況,所以在AddObject時是先將隱藏值放到SL[]再AddObject進去。但是後來經過實驗,發現還是會有偶爾出現亂碼的時候,尤其是第二次取得值的時候。
procedure TForm1.Button2Click(Sender: TObject);
var i:Integer;
begin
Memo1.Lines.Text:='';
Memo1.Lines.Add('你選擇了:');
for i := 0 to CheckListBox1.Items.Count - 1 do begin
if CheckListBox1.Checked[i] then begin //找出被選擇的選項
//Memo1.Lines.Add(string(CheckListBox1.Items.Objects[i]));//原本的程式
Memo1.Lines.Add(PChar(CheckListBox1.Items.Objects[i])); //取出Objects中的值
end;
end;
end;


  原本第一篇中是使用string的格式來取出,但再經過搜尋網路上的文章,發現string也會造成亂碼問題,所以將string改成使用PChar,問題就不再發生了。這一篇文章是從大富翁論壇來的(參考),雖然有點和我要問的不太一樣,不過其中PChar的論調卻派上用場了。以下就是最關鍵的一段回應:
將s:string;改為S: PChar就一切OK了, String是由Delphi自動管理的, 它不是簡單的指針, PChar才是指針


  以上四步大約就完成了一整個CheckListBox的程式,而以下是附加說明的,關於刪除選項的方法。
SL.Delete(5) ; //SL也要一併刪除,否則指標會指錯,而CheckListBox也會發生溢位問題
CheckListBox1.Items.Delete(5) ; //將第5項刪除




2 則留言:

  1. 謝謝delphi 高手! 以後再來學習 現在看不懂 . 高深啊 !

    回覆刪除
  2. 說高手不敢當,我也是向網路上的先輩偷師而來的。

    回覆刪除