Arsip: Mencegah Flicker dan Mempercepat Operasi


by DonVall in Articles more 18 years ago 2616
Emosi kan kalo liat tampilan kontrol di aplikasi kita berkedap-kedip (flicker) saat melakukan operasi yang panjang? Selain flicker itu mengganggu kenyamanan mata, pada sebagian besar kasus flicker itu berarti ada resource (baca: cpu cycle) yang dipakai secara sia-sia. Bila penggunaan resource yang sia-sia ini bisa dihindari atau direduksi maka operasi kita yang panjang dan intensif tsb akan dapat diselesaikan lebih cepat.

Banyak teknik yang dapat digunakan untuk menghindari flicker dan mempercepat operasi.

1. Gunakan BeginUpdate dan EndUpdate pada TStrings.

Pada proses yang menambahkan/menghapus/merubah sejumlah besar string ke/dalam obyek-obyek turunan TStrings, seperti property lines pada TMemo atau items pada TComboBox, bungkuslah operasi tersebut dalam metode BeginUpdate dan EndUpdate dari obyek TStrings tsb.
Contoh:

(             
Kode berikut menambahkan string berisi nomer 1 s.d. 5000
ke dalam sebuah combobox (ComboBox1)
               )
procedure TForm1.Button2Click(Sender: TObject);
Var
i: Integer;
begin
ListBox1.Items.BeginUpdate;
Try
For i := 1 to 5000 do ListBox1.Items.Add(IntToStr(i));
Finally
ListBox1.Items.EndUpdate;
End;
end;

Pada dasarnya, setiap kali sebuah string ditambah maka tampilan ListBox1 harus diperbaharui agar string yang baru ikut ditampilkan. Jika penambahan ini dilakukan dalam jumlah besar dalam satu waktu, tentu proses penyegaran tampilan ListBox secara berulang2 merupakan proses yang sia-sia. Di sini lah gunanya fungsi BeginUpdate.

Dengan memanggil metode BeginUpdate, obyek TStrings yang menjadi properti Items di ListBox disuruh untuk tidak memberitahukan perubahan pada isi array string-nya kepada kontrol ListBox pemiliknya. Dengan demikian kontrol ListBox tidak akan menyegarkan tampilannya. Metode EndUpdate sebaliknya menyuruh object TStrings ybs untuk memberitahu kepada kontrol pemiliknya kemungkinan adanya perubahan pada isi array string. Yang pada gilirannya menyuruh kontrol ListBox untuk menyegarkan tampilannya.

Secara sederhana, pada contoh kode di atas, tanpa BeginUpdate dan EndUpdate, aplikasi akan memperbaharui tampilan dari ListBox1 sebanyak 5000x. Yang 4999 di antaranya adalah sia-sia dan bisa jadi merupakan sumber flicker (tergantung kecepatan PC-nya). Sedang dengan BeginUpdate dan EndUpdate aplikasi hanya memperbaharui tampilan ListBox sebanyak 1 kali saja tanpa flicker (untuk kebanyakan PC masa kini. Ada yang masih pake XT?).

2. Gunakan DisableControls dan EnableControls pada turunan TDataset.

Pada operasi yang melibatkan pemindahan penunjuk record dalam jumlah besar seperti contoh di bawah:
Dataset1.First;
While not Dataset1.eof do
Begin ... ... Dataset1.Next; End;

Flicker pada kontrol-kontrol database yang terlibat dapat dihindari dan peningkatan kecepatan operasi dapat diperoleh dengan membungkus operasi tsb dalam blok DisableControls dan EnableControls, seperti berikut:
With Dataset1 do
Begin DisableControls; Try First; While not eof do
Begin ... ... Next End; Finally EnableControls End; End;


Metode DisableControls menyuruh dataset tsb untuk tidak melaporkan perubahan record yang aktif. Sehingga kontrol database, seperti dbgrid, yang terhubung dengannya tidak akan melakukan penyesuaian tampilan. Yang berarti penghematan CPU cycle untuk penyegaran tampilan kontrol database yang tidak perlu. Sedang metode EnableControls menyuruh dataset untuk melaporkan kemungkinan adanya perubahan record yang aktif, sehingga kontrol-kontrol database yang terhubung dengannya bisa menyesuaikan tampilannya.

3. Gunakan Window message WM_SETREDRAW untuk kontrol2 yang tidak mempunyai dukungan built-in seperti 2 point sebelum ini.

Banyak kontrol grafis yang tidak memiliki dukungan built-in seperti pada point 1 dan 2 di atas. Untuk kontrol seperti ini dapat digunakan Window Message WM_SETREDRAW. Sebelum memasuki operasi yang panjang dan intensive, kirimkan window message WM_SETREDRAW ke kontrol ybs parameter WParam dan LParam bernilai 0. Hal ini memberitahukan kontrol tersebut untuk tidak memproses message-message yang menyuruhnya untuk memperbaharui tampilan.
Sesudah operasi yang panjang dan intensive selesai, kirimkan WM_SETREDRAW kembali ke kontrol ybs dengan parameter WParam=1 dan LParam=0.
Hal ini menyebabkan kontrol tersebut memproses message untuk memperbaharui tampilan yang akan diterimanya.
Contoh:
StringGrid1.Perform(WM_SETREDRAW, 0, 0);  // jangan perbaharui tampilan
Try
...  // di sini dilakukan operasi penambahan isi string grid
...
Finally
StringGrid1.Perform(WM_SETREDRAW, 1, 0); // boleh perbaharui tampilan
StringGrid1.Invalidate;  // perbaharui tampilan
End;

Sama seperti point 1 dan 2 sebelumnya, penggunaan message WM_SETREDRAW ini menghindari penggambaran kontrol berulang-ulang yang sia-sia, menjadi penggambaran sekali saja di akhir proses.

4. Menggunakan perintah WinAPI LockWindowUpdate Perintah LockWindowUpdate akan mengunci/membuka kunci perubahan tampilan sebuah window.

Saat dikunci tampilan window ybs tidak akan berubah sampai kuncinya dibuka. Hal ini sangat berguna bila operasi yang dilakukan melibatkan beberapa kontrol atau window sekaligus. Karena dengan mengunci window yang menjadi parent dari semua kontrol yang terlibat, maka flicker yang disebabkan oleh perubahan isi atau posisi dari kontrol-kontrol di dalamnya dapat dihindari.
Dalam penggunaannya LockWindowUpdate memerlukan window handle dari window yang akan dikunci. Sedang untuk membuka penguncian tampilan dapat dilakukan dengan memberikan nilai 0 untuk window handle ini.

Dalam satu saat hanya satu window yang dapat dikunci. Dan ini berlaku untuk keseluruhan sistem, tidak hanya pada aplikasi ybs saja. Untuk menghindari sebuah aplikasi membuka kunci yang pengunciannya dilakukan oleh metode lain (atau bahkan aplikasi lain), saat mengunci periksa dulu apakah proses penguncian berhasil atau tidak. Hal ini diindikasi kan dari nilai boolean yang dikembalikan oleh fungsi LockWindowUpdate ini. Bila memang metode yang sama yang melakukan penguncian maka di akhir proses dapat dilakukan pembukaan kunci.

Contoh:
Procedure TForm1.LoadManyManyGraphicControls;
Var
lbLocked: Boolean;
Begin
lbLocked := LockWindowUpdate(Self.Handle);
Try
....
....
....
Finally
If lbLocked Then LockWindowUpdate(0);
End;
End;


Catatan:
a. saat kunci dibuka, tampilan seluruh layar akan diperbaharui (bukan hanya window yang dikunci).
b. Pada beberapa kasus penggunaan LockWindowUpdate dilaporkan malah menimbulkan Flicker, namun hal ini dapat diatasi dengan menge-set property DoubleBuffering dari Form yang bersangkutan menjadi True (default-nya Delphi false).

Catatan akhir:
Dari pengalaman, penulis menyarankan prioritas penggunaan teknik2 di atas sesuai dengan urutannya. Jadi usahakan menggunakan teknik no. 1, kalau tidak bisa coba no. 2, 3, dan 4.

selamat mencoba.

Local Business Directory, Search Engine Submission & SEO Tools FreeWebSubmission.com SonicRun.com