Arsip: Membuat kontrol db aware


by DonVall in Articles more 15 years ago 1532
Kontrol yang bersifat db-aware sangat nyaman digunakan. Cukup letakkan di dalam form, set property datasource ke TDatasource yang tepat, serta mungkin perlu menentukan nama field yang ingin digunakan/ditampilkan, dan.. Voila! Hampir tanpa perlu menulis kode apa pun fungsi dasarnya sudah bisa dinikmati!
Membuat kontrol db aware

Kontrol yang bersifat db-aware sangat nyaman digunakan. Cukup letakkan di dalam form, set property datasource ke TDatasource yang tepat, serta mungkin perlu menentukan nama field yang ingin digunakan/ditampilkan, dan.. Voila! Hampir tanpa perlu menulis kode apa pun fungsi dasarnya sudah bisa dinikmati!

Dalam artikel kecil ini kita akan mempelajari bagaimana membuat kontrol yang bersifat db aware dari kontrol VCL standar. Pengetahuan ini akan sangat berguna bila suatu saat kita membutuhkan kontrol vcl standar yang mengambil data/informasi dari database. Seperti misalnya TDBCheckListBox yang akan dijadikan contoh dalam artikel ini.

TDBCheckListBox memiliki tugas sederhana, yakni mengambil daftar item dari database. Kemudian menampilkan daftar ini berserta checkbox sehingga pengguna dapat menggunakan mouse untuk memilih atau membatalkan pilihan.

Ya, saya tahu, pasti sebagian besar dari anda berkata, "Apa masalahnya? Kan tinggal baca aja satu persatu record-nya lalu masukkan ke dalam sebuah TCheckBoxes?". Penyelesaian tersebut memang benar. Tetapi, bayangkan jika kebutuhan akan daftar beserta checkbox ini tidak hanya muncul 1 atau 2 kali? Bukankah berarti penulisan kode berulang-ulang? Ingat bahwa semakin banyak menulis kode berarti semakin banyak kemungkinan timbulnya bug, dan berarti semakin besar tugas untuk maintenance.

Dengan dipusatkannya kode di satu kontrol db-aware memudahkan tugas debugging, tugas maintenance, dan juga tugas pengembangan di kemudian hari. Bayangkan kalau pelanggan atau boss anda tiba2 minta agar tampilan kontrol anda dirubah. Bila kode anda tersebar di 3 form, berarti anda harus mencari di form-form mana kode yang harus diganti dan menggantinya... dengan kode yang tepat sama, mungkin harus menyesuaikan dengan nama2 variabel yang berbeda di masing2 tempat, baru kemudian recompile. Membosankan, bukan?

Dengan menggunakan kontrol/komponen yang kode-nya terpusat anda cukup melakukan perubahan di satu tempat, recompile proyek tsb, dan.. selesai! Belum lagi tersedianya bantuan property editor.

Nuff said! Let's move on!

Mari definisikan kebutuhan kita untuk TDBCheckListBox. TDBCheckListBox kita harus bisa:
1. Bisa menampilkan daftar item (string) yang didampingi checkbox untuk penanda apakah item ybs terpilih atau tidak.
2. Bisa mengambil daftar item dari dataset yang terhubung dengannya.
3. Bila dataset yang terhubung aktif maka TvrlDBCheckbox akan memperbaharui daftarnya sesuai dengan isi dataset tsb.
4. Bila dataset yang terhubung tidak aktif (closed) maka TvrlDBCheckbox bisa mengosongkan daftar pilihan.
5. TvrlDBCheckBoxes bisa diatur untuk tetap mempertahankan daftar pilihannya yang telah diambilnya walau dataset yang terhubung dengannya ditutup.
6. TvrlDBCheckBoxes dapat memberikan informasi kunci tentang item-item yang terpilih (checkbox terconteng).

Dari definisi di atas, kita bisa susun kerangka untuk kontrol db aware kita, dengan pertimbangan untuk memenuhi kebutuhan no. 1 maka kontrol kita akan diturunkan dari TCheckListBox (bisa menampilkan daftar string dengan didampingi checkbox).

Type
TDBCheckListBox = Class(TCheckListBox)
Public
// menghasilkan integer yang merupakan informasi kunci dari item pada posis index tertentu
// Untuk memenuhi kebutuhan no. 6
Property KeyValue[Index: Integer]: Integer Read GetKeyValue;
Published
// property tempat menentukan atau mengetahui datasource yang digunakan untuk mengakses
// dataset (untuk kebutuhan no. 2)
Property DataSource: TDataSource;
// property untuk menentukan atau mengetahui nama field yang isinya digunakan sebagai
// pengisi daftar item (untuk kebutuhan no. 2)
Property DataField: String;
// property untuk menentukan atau mengetahui nama field yang isinya digunakan sebagai
// informasi kunci dari item dalam daftar (untuk kebutuhan no. 6)
Property KeyField : String;
// property sebagai penanda apakah daftar yang sudah diambil dari dataset harus dipertahankan
// ataus dihapus saat dataset yang terhubung ditutup (kebutuhan no. 4 dan 5)
Property RetainItems: Boolean;
End;


Berikutnya kita membutuhkan bantuan untuk menghubungkan kontrol kita dengan event-event di dataset (via TDataSource yang di-set di properti DataSource). Di Delphi telah disediakan 2 kelas untuk ini. Yakni TDataLink dan TFieldDataLink. Fungsi kedua kelas ini sama, hanya saja TFieldDataLink lebih dikhususkan untuk kebutuhan yang terkait pada 1 field saja. Bila kita tidak membutuhkan informasi spesifik dari field tertentu kita dapat menggunakan TDataLink untuk menghubungkan event2 dataset ke kontrol kita.

Pada kontrol kita, kita membutuhkan 2 buah TFieldDataLink. Satu untuk menangani DataField (yang akan ditampilkan), dan yang lainnya untuk menangani KeyField (yang menyediakan informasi kunci).

Pada TFieldDataLink property yang perlu diperhatikan adalah:
- DataSource, menentukan TDataSource yang menyediakan hubungan ke dataset
- FieldName, menentukan nama field yang datanya akan dimanipulasi via TFieldDataLink ybs.

Sedang TFieldDataLink akan membangkitkan event-event penting berikut:
- OnActiveChange, yang akan dibangkitkan saat status keaktifan dataset berubah
- OnDataChange, yang akan dibangkitkan saat isi field berubah.
- OnEditingChange, yang akan dibangkitkan saat status editing field ybs berubah. Biasa terjadi saat dataset ybs memasuki modus editing (dataset.edit), insert, post, atau batal (cancel).
- OnUpdateData, yang akan dibangkitkan saat perubahan pada record atau field akan disimpan (post).

Karena kontrol kita tidak akan berhubungan dengan proses editing/pengubahan data, maka cukup event OnActiveChange saja yang dihandle. Dengan menangani event ini kita bisa memenuhi kebutuhan no. 2, 3, dan 4. Karena kita akan menggunakan 2 buah TFieldDataLink, tidak berarti event OnActiveChange keduanya harus ditangani. Cukup satu saja, karena keduanya terhubung ke datasource/dataset yang sama.

Dengan begitu kerangka kontrol kita bisa diperbaharui menjadi:

TDBCheckListBox = Class(TCheckListBox)
Private
// deklarasi obyek TFieldDataLink yang menyediakan event2 yang terjadi di dataset
FDataLink: TFieldDataLink;
FKeyLink: TFieldDataLink;

FRetainItems: Boolean;

procedure SetDataSource(const Value: TDataSource);
function GetDataSource: TDataSource;

function GetDataField: String;
procedure SetDataField(const Value: String);

function GetKeyField: String;
procedure SetKeyField(const Value: String);

function GetKeyValue(Index: Integer): Integer;
Protected
// handler untuk event OnActiveChange
Procedure ActiveChange(Sender: TObject);
// metode untuk menambahkan item ke dalam daftar beserta informasi kuncinya
Procedure Add(Const AnItem: String; Const AKey: Integer);
Public
Constructor Create(AOwner: TComponent); Override;
Destructor Destroy; Override;

Property KeyValue[Index: Integer]: Integer Read GetKeyValue;
Published
Property DataSource: TDataSource read GetDataSource write SetDataSource;
Property DataField: String Read GetDataField Write SetDataField;
Property KeyField : String Read GetKeyField Write SetKeyField;
Property RetainItems: Boolean Read FRetainItems Write FRetainItems;
End;



Berikutnya kita akan membahas bagaimana implementasi kerangka kontrol kita.

Constructor kita bertanggung jawab untuk menyediakan 2 buah TFieldDataLink, dan membuat procedure ActiveChange menjadi handler dari event OnActiveChange-nya FDataLink.
constructor TDBCheckListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FKeyLink := TFieldDataLink.Create;
FDataLink := TFieldDataLink.Create;
FDataLink.OnActiveChange := ActiveChange;
end;


Destructor kita akan bertanggung jawab untuk membebaskan resource yang digunakan oleh kontrol kita dengan baik. Kelas TFieldDataLink tidak memiliki mekanisme untuk otomatis memanggil destructor-nya. Karenanya pembuatnya bertanggung jawab untuk menghancurkannya.

destructor TDBCheckListBox.Destroy;
begin
FDataLink.Free;
FKeyLink.Free;
inherited;
end;


Property DataSource. Karena yang langsung berhubungan dengan datasource adalah kedua TFieldDataLink kita, maka cukup diteruskan saja pemberian atau pembacaan obyek TDatataSource kepada keduanya.

function TDBCheckListBox.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;

procedure TDBCheckListBox.SetDataSource(const Value: TDataSource);
begin
FKeyLink.DataSource := Value;
FDataLink.DataSource := Value;
end;


Property DataField. Yang berhubungan dengan field ini adalah FDataLink. Sehingga operasi pembacaan atau pemberian nama data field cukup diteruskan kepadanya.

function TDBCheckListBox.GetDataField: String;
begin
Result := FDataLink.FieldName;
end;

procedure TDBCheckListBox.SetDataField(const Value: String);
begin
FDataLink.FieldName := Value;
end;


Property KeyField. Yang berhubungan dengan field penyedia informasi kunci ini adalah FkeyLink. Sehingga pembacaan atau pemberian nama field ini cukup diteruskan kepadanya.

function TDBCheckListBox.GetKeyField: String;
begin
Result := FKeyLink.FieldName;
end;

procedure TDBCheckListBox.SetKeyField(const Value: String);
begin
FKeyLink.FieldName := Value;
end;


Property RetainItems. Di sini kita menggunakan sebuah field variabel local untuk menyimpan nilai ini.

Procedure Add. Di sini kita menambahkan sebuah item (string) ke daftar kita dan menyimpan informasi kuncinya. Untuk menyimpan nilai integer dari informasi kunci ini kita fungsi penyimpanan referensi object yang disediakan oleh property Items dari TcheckListBox. Untuk itu kita harus merubah tipe (casting) dari integer menjadi Tobject.

procedure TDBCheckListBox.Add(const AnItem: String; const AKey: Integer);
Var
vDummyObject;
begin
vDummyObject := Tobject(AKey); // kita cast nilai integer seakan2 menjadi referensi object
Self.AddItem(AnItem, vDummyObject); // tambahkan item dan dummy object
end;


Procedure ActiveChange. Di sini lah fungsi utama dari kontrol kita dilakukan. Di sini kita melakukan pembacaan record-record dari dataset untuk mengisi daftar kita atau membersihkan daftar kita.

procedure TDBCheckListBox.ActiveChange(Sender: TObject);
Var
vBookMark: TBookMark;
vAddWithKey: Boolean;
begin
// jika dataset aktif, siap-siap untuk isi daftar
If FDataLink.Active Then With FDataLink.DataSet do Begin
Items.BeginUpdate; // cegah penyegaran tampilan yang tidak perlu
Try
Clear; // hapus isi daftar yang sudah ada.
// cek apakah field informasi kunci disediakan, bila tidak penambahan item tidak perlu
// dengan informasi kuncinya.
vAddWithKey := Assigned(FKeyLink.Field);
DisableControls;
Try
// tandai posisi record aktif, agar bisa kembali lagi
vBookMark := FDataLink.DataSet.GetBookmark;
Try
First;

// selagi belum sampe record akhir, tambahkan item beserta atau tanpa informasi kunci
While not eof do Begin
If vAddWithKey Then
Self.Add( FDataLink.Field.AsString, FKeyLink.Field.AsInteger)
Else
Self.Add( FDataLink.Field.AsString,0);

Next;
End;
GotoBookmark(vBookMark); // kembali ke record yang aktif sebelumnya
Finally
FreeBookMark(vBookMark);
End;
Finally
EnableControls;
End;
Finally
Items.EndUpdate;
End;
End
Else
// kalau dataset tidak aktif, cek apakan isi daftar lama harus dipertahankan atau tidak
// jika tidak, hapus daftar item yang sudah ada
If not FRetainItems Then Clear;
end;



Source code lengkap dari TDBCheckListBox bisa didownload di sini .
Catatan:
- isi source code asli dari TDBCheckListBox mungkin sedikit berbeda dengan yang diterangkan di sini. Karena di sini banyak fungsi yang dihapus untuk bisa memperoleh fokus pada pembahasan.
- Informasi kunci yang digunakan hanya bertipe integer, karena standar internal dari penulis sendiri bahwa primary field harus integer. Namun tidak tertutup kemungkinan untuk menggunakan tipe data variant. Sehingga bermacam-macam tipe data bisa digunakan untuk informasi kunci ini.

Komentar, saran, atau pun kritik sangat dinanti, demikian juga pengembangan dari TDBCheckListBox ini sendiri.

Pesan dari penulis
( )
( Harap dihapus dari artikel bila dimuat
)
Catatan untuk admin/editor:

Saya sangat menghormati standarisasi pengkodean yang digunakan di situs ini (delphi-id.com). Tetapi saya juga mengharapkan hal yang sama dari anda terhadap standar pengkodean yang saya gunakan. Jadi artikel ini boleh dirubah tata letaknya agar lebih enak dibaca, atau beberapa kalimatnya disesuaikan supaya lebih mudah dimengerti, tetapi saya mohon gaya penulisan kode saya tidak dirubah.

Bila permintaan saya tidak bisa dipenuhi, harap artikel ini ditolak saja.

Satu lagi, di mana saya bisa upload source code? dan link-nya bisa di tampilkan dalam artikel ini?

Terima kasih,

Luthfi B Hakim

(
)
(
Akhir catatan untuk admin/editor
*)
( )
[red: untuk kali ini artikel anda kami tampilkan apa adanya]
Local Business Directory, Search Engine Submission & SEO Tools FreeWebSubmission.com SonicRun.com