วันพุธที่ 4 มีนาคม พ.ศ. 2558

บันทึก/โหลด ค่าพร็อพเพอตี้ของคอนโทรลต่างๆ บนฟอร์ม

พอดีมีเพื่อนที่มหาลัยที่เขียน Delphi ด้วยกัน Line มาถามผมว่า "อยากให้ค่าต่างๆ ของคอนโทรลต่างๆบนฟอร์มไม่ถูกลบตอนปิดโปรแกรม พอเปิดโปรแกรมขึ้นมาอีกครั้งให้ค่าพวกนั้นมันอยู่เหมือนเดิมได้มั้ย" ผมก็รีบตอบเลยครับว่า "ได้" จริงมีวิธีอยู่หลายวิธีนะครับ แต่วันผมจะใช้วิธี เก็บบันทึกค่า Property ที่ต้องการของ Control ทั้งหมดที่อยู่บนฟอร์ม ไว้ที่ไฟล์ ini หรือ Initialize File  ซึ่งมันก็คือ Text File ธรรมดาๆนี่เองแหละครับ โดยไฟล์เหล่านี้จะมีนามสกุลเป็น INI ซึ่งเราจะบันทึกค่าล่าสุดไว้ก่อนปิดโปรแกรม และโหลดมันขึ้นมาตอนเปิดโปรแกรมง่ายๆ อย่างงี้เลยครับ จริงๆวิธีการนี้ก็มีคนเค้าใช้กันมานานแล้ว อาจจะเก่าไปแล้วด้วยซ้ำ เนื่องจากถูกเอกสาร XML (eXtensive Markup Language) เข้ามาแทนที่มันไปแล้วครับ แต่ว่าวิธีนี้ก็ยังใช้ได้ดีแล้วที่สำคัญคือมันเข้าใจได้อย่างง่ายดายเลยทีเดียว

INI File จะมีส่วนสำคัญ 3 ส่วน ดังนี้คือ

  1. Section ส่วนของชื่อที่ปิดด้วยเครื่องหมาย Bracket [] ... เพื่อตั้งชื่อเป็นกลุ่มข้อมูล
  2. Key เป็นชื่อเฉพาะที่มีค่าไม่ซ้ำกัน หรือ ก็คือตัวแปรดีๆนี่เองแหละครับ
  3. Value เป็นค่าที่ถูกกำหนดให้กับ Key เพื่อใช้ในการอ่าน และ เขียนเข้าไปใน INI File

เริ่มกันเลยครับ

1. สร้างโปรเจคขึ้นมาก่อนแล้ววาง Control ต่างตามรูปด้านล่างเลยครับ เอาเฉพาะ Control มาตรฐานนะครับ ซึ่งเราจะเก็บค่าสำคัญๆ ของ Control เหล่านั้นไว้

2. กด F12 เข้าไปในส่วน Code ของโปรแกรม ไปที่ส่วน Interface ของโปรแกรมให้ Uses ยูนิตไฟล์ IniFiles เข้าด้วยครับ


3. ประกาศ Array ไว้เก็บชื่อ Property ของ Control ทั้งหมดบนฟอร์มไว้ 
ใส่ชื่อ Property ที่สำคัญๆ ที่ต้องการเก็บตามรูปด้านล่างเลยครับ เช่นว่า ถ้าเป็น EditText ก็อาจจะเก็บ ค่า Text หรือ CheckBox อาจจะเก็บค่า Checked ไว้ ว่าถูกติ๊กอยู่หรือไม่ หรืออาจจะเก็บค่า Position หรือตำแหน่งของ Trackbar เอาไว้ 
ส่วนต่อมาให้ประกาศฟังก์ชั่น ขึ้นมา 4 ตัว ตามรูปเลยครับ เอาไว้ Load / Save ค่า Control ต่างๆ 



4. ในส่วน implementation เขียน Code ของฟังก์ชั่น SaveControls ตามนี้เลยครับ เอาไว้บันทึกค่า ของ Property ใน Control ลงไฟล์ ini

5. ตามด้วยเขียน Code ของฟังก์ชั่น LoadControls ตามนี้เลยครับ เอาไว้โหลดค่า ของ Property  Control จากไฟล์ ini มาสู่ Control บนฟอร์ม

6. สุดท้าย เขียนอีก 2 ฟังก์ชั่นที่เหลือ ครับ แล้วพอเราจะใช้งานกันจริงๆ ก็ไปที่ Event OnClose กับ OnCreate ของ Form ครับ ใน OnClose เราจะบันทึกค่า Property ของ Control ก่อนปิดโปรแกรม ดังนั้นเราจึงเรียกใช้ฟังก์ชั่น SaveControlsToFile() และพอเวลาโปรแกรมจะเปิด เราก็ไปโหลดค่าต่างที่เก็บใน ini เข้าสู่ Control โดยใช้ฟังก์ชั่น LoadControlsFromFile() ซึ่งทั้ง 2 ฟังก์ชั่นจะรับพารามิเตอร์ 2 ตัวคือ 1) ชื่อไฟล์ ini และ 2) ตัว Form ที่ต้องการเก็บ Control ซึ่งคำว่า Self ก็คือตัวอ้างอิง Object ของ Class TForm1 ซึ่ง สืบทอดมาจาก Class TForm นั่นเอง หรือพูดให้เข้าใจง่ายๆก็คือ ตัวมันเองหรือฟอร์มๆนั้น นั่นแหละครับ



7. ลองทดสอบโปรแกรมดูครับ พอเรา Run โปรแกรมครั้งแรก ค่า Default ของ Control ต่างๆ บนฟอร์มคงยังเหมือนเดิมกับตอนออกแบบครับ แล้วลองเข้าไปดูที่ Folder เก็บ Exe ไฟล์ของโปรแกรมเราจะสังเกตว่ายังไม่การสร้างไฟล์ ini เลยครับ

8. ที่นี้เราลองเปลี่ยนแปลงค่าต่างๆ ของ Control แล้วปิดโปรแกรมครับ


10. ทีนี้เราลองมาดูข้อมูลในไฟล์ ini ครับ

11. ทีนี้ถ้าเราเปิดโปรแกรมครั้งต่อไป ค่าต่างๆที่บันทึกใน ini ก็จะถูกโหลดเข้าสู่ Control บนฟอร์มเองครับ


Source code ของโปรแกรมครับ เขียนด้วย XE7 นะครับ
 unit Unit1;  
 interface  
 uses  
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,  
  System.Classes, Vcl.Graphics,  
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,  
   Vcl.ComCtrls, cxTextEdit, cxCurrencyEdit, IniFiles, cxGraphics, cxControls,  
  cxLookAndFeels, cxLookAndFeelPainters, cxContainer, cxEdit;  
 type  
  TForm1 = class(TForm)  
   Edit1: TEdit;  
   CheckBox1: TCheckBox;  
   cxCurrencyEdit1: TcxCurrencyEdit;  
   TrackBar1: TTrackBar;  
   Memo1: TMemo;  
   Label1: TLabel;  
   Label2: TLabel;  
   Label3: TLabel;  
   Label4: TLabel;  
   procedure FormCreate(Sender: TObject);  
   procedure FormClose(Sender: TObject; var Action: TCloseAction);  
  private  
   { Private declarations }  
  public  
   { Public declarations }  
  end;  
 var  
  Form1: TForm1;  
 const CONTROL_PROPS: array[0..9] of string =  
    ( 'Left',  
     'Top',  
     'Width',  
     'Height',  
     'Visible',  
     'Caption',  
     'Text',  
     'Checked',  
     'Value',  
     'Position'  
    );  
  procedure SaveControls(toIniFile: TIniFile; fromForm: TForm);  
  procedure LoadControls(fromIniFIle: TIniFile; toForm: TForm);  
  procedure SaveControlsToFile(const FileName: string; fromForm: TForm);  
  procedure LoadControlsFromFile(const FileName: string; toForm: TForm);  
 implementation  
 {$R *.dfm}  
 uses TypInfo;  
 procedure SaveControls(toIniFile: TIniFile; fromForm: TForm);  
 var  
  i, j : integer;  
  obj : TComponent;  
  s, sec : string;  
 begin  
    // เก็บชื่อ Section  
    sec := fromForm.Name;  
    // วนลูปไปบนคอมโพเน้นท์ทุกตัวบนฟอร์ม  
    for i := 0 to fromForm.ComponentCount -1 do begin  
    // เก็บค่า Reference (ค่าอ้างอิงตัวคอมโพเน้นท์)  
      obj := fromForm.Components[i];  
    // วนลูปเข้าสู่ Property ที่ประกาศไว้บน Array  
      for j := Low(CONTROL_PROPS) to High(CONTROL_PROPS) do  
    // เช็คว่าคอมโพเน้ท์มีพร็อเพอตี้ที่ประกาศไว้หรือไม่  
        if IsPublishedProp(obj, CONTROL_PROPS[j]) then begin  
    // เก็บค่าในรูปแบบ "ชื่อคอมโพเน้นท์.พร็อพเพอตี้"  
         s := Format('%s.%s', [obj.Name, CONTROL_PROPS[j]]);  
    // เขียนข้อมูลลงไฟล์ ini  
         toIniFile.WriteString(sec, s, GetPropValue(obj, CONTROL_PROPS[j]));  
        end;  
    end;  
 end;  
 procedure LoadControls(fromIniFIle: TIniFile; toForm: TForm);  
 var i, j : integer;  
   obj : TComponent;  
   s, sec, value : string;  
 begin  
    // เก็บชื่อ Section  
    sec := toForm.Name;  
    // วนลูปไปบนคอมโพเน้นท์ทุกตัวบนฟอร์ม  
    for i := 0 to toForm.ComponentCount -1 do begin  
    // เก็บค่า Reference (ค่าอ้างอิงตัวคอมโพเน้นท์)  
      obj := toForm.Components[i];  
    // วนลูปเข้าสู่ Property ที่ประกาศไว้บน Array  
      for j := Low(CONTROL_PROPS) to High(CONTROL_PROPS) do  
    // เช็คว่าคอมโพเน้ท์มีพร็อเพอตี้ที่ประกาศไว้หรือไม  
        if IsPublishedProp(obj, CONTROL_PROPS[j]) then begin  
    // เก็บค่าในรูปแบบ "ชื่อคอมโพเน้นท์.พร็อพเพอตี้"  
         s := Format('%s.%s', [obj.Name, CONTROL_PROPS[j]]);  
    //อ่านข้อมูลจากไฟล์ ini ตาม Section และ Identity  
         value := fromIniFIle.ReadString(sec, s, EmptyStr);  
    // ตรวจสอบค่าที่อ่านว่ามีอยู่ใน ini หรือไม่  
         if value <> EmptyStr then  
    // กำหนดค่า ลง Property ของ Component นั้นๆ  
           SetPropValue(obj, CONTROL_PROPS[j], value);  
        end;  
    end;  
 end;  
 procedure SaveControlsToFile(const FileName: string; fromForm: TForm);  
 var ini : TIniFile;  
 begin  
    ini := TIniFile.Create(ExtractFilePath(Application.ExeName)+FileName);  
    SaveControls(ini, fromForm);  
    FreeAndNil(ini);  
 end;  
 procedure LoadControlsFromFile(const FileName: string; toForm: TForm);  
 var ini : TIniFile;  
 begin  
    ini := TIniFile.Create(ExtractFilePath(Application.ExeName)+FileName);  
    LoadControls(ini, toForm);  
    FreeAndNil(ini);  
 end;  
 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);  
 begin  
  SaveControlsToFile('config.ini', Self);  
 end;  
 procedure TForm1.FormCreate(Sender: TObject);  
 begin  
  LoadControlsFromFile('config.ini', Self);  
 end;  
 end.  




1 ความคิดเห็น: