Categories
Article

Bagamana nak buat program GUI Ringkas dengan C# dan .Net Framework

WARNING: Saya rasa ada banyak kesilapan ejaan dan keterlajuan dalam article ini. So… good luck.

Assalamualaikum semua! Apa khabar! Pada hari ini, saya akan tulis tentang bagaimana untuk buat program Graphical User Interface (GUI) yang ringkas dengan C#. Article ini adalah terjemahan dari article saya yang lain kat RookieCode, so kemungkinan besar akan ada banyak masalah terjemahan.


Oh, mari kita mulakan. Mula-mula sekali mari kita mengenal C# dan sejarahnya. C#(disebut C-sharp) merupakan satu bahasa programming yang agak baru. Bahasa ini diperkenalkan oleh Microsoft sebagai bahasa rasmi .Net Framework mereka. C# bertujuan untuk menggabungkan kelebihan-kelebihan C++ dan Java dalam satu platform. Ia lebih laju dari Java dan jauh lebih mudah untuk digunakan dari C++. Seperti Java, C# bukanlah compiled language semata-mata. Ianya adalah gabungan compiled language dan interpreted language. Source Code C# tidak di-compile terus kepada machine-code, tetapi ianya di-compile kepada sejenis bytecode khas yang mana akan dicompile pula kepada machine code oleh .Net CLR(Common Language Runtime) ketika program itu dijalankan. Disebabkan ini, program C# boleh di-optimumkan kepada CPU pengguna tanpa perlu binary berlainan. .Net Framework (disebut Dot-Net framework), merupakan satu kolleksi library/API yang boleh diakses daripada beberapa programming language yang menyokong .Net Framework. Bahasa utamanya adalah C#, namun ia juga boleh digunakan dengan bahasa programming yang lain seperti C++, J# dan Visual Basic .Net. Bahasa programming tersebut perlu dicompile kepada bytecode khas sepertimana juga C#. Ini membolehkan bahasa programming lain mengakses utility yang dibuat menggunakan bahasa programming yang lain dengan cara yang sangat portable. .Net Framework sudah diinstall secara default atas Windows XP service pack 2 dan keatas. Windows XP sebelum service pack 2 boleh download .Net Framework secara berasingan. .Net Framework tidak boleh digunakan untuk Windows Millennium dan sebelumnya. Untuk Linux, satu projek yang dipanggil “mono” membolehkan program .Net untuk berfungsi walaupun dengan sedikit masalah compatibility.

Di post ini, kita akan menggunakan Microsoft Visual C# Express Edition untuk buat program kita. Edisi Express boleh didapati secara percuma di sini . Microsoft Visual Studio ialah koleksi IDE(Integrated Development Environment) untuk pembangunan program, Lesen standardnya tidak percuma walaupun ianya mungkin adalah IDE terbaik sekali.

Baiklah, mari kita mulakan. Start Visual C#, anda akan jumpa screen seperti dibawah.

Tekan butang “New Project”.

Pilih “Windows Forms Application” dan tukar nama projek kepada “MyMouseFollower” atau apa-apa yang kamu suka.Tekan “OK” dan screen pertama yang sepatutnya kamu jumpa ialah form builder seperti dibawah.

Form builder adalah satu peralatan yang membolehkan anda untuk menyusun komponen program anda dengan mudah tanpa menggunakan kod directly. Di bahagian atas kiri form builder ada satu butang “Toolbar”. Tekan butang tersebut dan senarai “Control” yang kamu boleh gunakan akan muncul.

Untuk projek ini, kita akan menggunakan “Panel”. Secara ringkasnya, Panel adalah ruang kosong yang mana kita akan lukis diatasnya nanti.Tekan “Panel” dan drag-drop atas “Form”

Juga, pilih “Label” dan letakkannya dibawah panel tadi.

Sekarang tulisan di label itu ialah ‘label1’ dan itu bukanlah apa yang kita nak. Right click diatasnya, kemudian tekan “properties”. Satu tab akan muncul di belah kanan screen.

Tab ini membolehkan anda untuk mengubah property Control yang dipilih. Edit property “Text” label1 kepada “My mouse follower” atau apa-apa yang kamu suka. Tulisan di label1 patut bertukar dari “label1” kepada “My mouse follower”.

Kamu boleh jalankan program tersebut menggunakan butang “Start Debugging” yang terletak di toolbar atas. Satu window yang berfungsi akan muncul walaupun dalam keadaanya sekarang ianya tidak berguna dan membosankan.

Baiklah, sekarang mari kita buat programming. Di bahagian kanan screen,

Ada tab yang menunjukkan file-file yang ada dalam projek. Pilih “Form1.cs” (file yang tengah kita pakai), right click-nya, dan tekan “View Code”.

Ini adalah kode untuk Form1 ditulis dalam C#. Lapan baris pertama terdiri dimulakan dengan perkataan “using”. Kata kunci “using” memberitahu compiler namespace mana yang kita gunakan. .Net Framework merangkumi banyak program/utility. Untuk memudahkan pengurusan program tersebut, utiliti-utiliti tersebut dikumpulkan dalam kumpulan yang dipanggil namespace. namespace bermaksud ruang nama. Ianya digunakan untuk mengelakkan kekeliruan antara class yang mempunyai nama yang sama dan juga untuk mengatur class yang sama jenis/kegunaan. Dalam kod diatas, class “Form1” terkandung antara dua “curly braces” yang dimulakan dengan perkataan “namespace MyMouseFollower”. Ini menunjukkan yang class Form1 terletak didalam namespace MyMouseFollower. class dari namespace lain boleh mengakses class Form1 menggunakan nama penuhnya iaitu “MyMouseFollower.Form1”. Ataupun, kita boleh letak baris “using MyMouseFollower” di permulaan source code class tersebut dan hanya menggunakan “Form1”. Kita akan lihat bagaimana ia dilakukan nanti.

Baris “public partial class Form1:Form” mengisytiharkan class yang bernama “Form1”. Dalam object oriented programming, class ialah kod yang “define” sesuatu object. Fikirkan perkataan “class” itu lebih kepada “kereta ber-class C” dan bukannya “class room”. Butiran Object Oriented Programming agak sukar untuk diterangkan di peringkan ini. Buat masa ini, aggap sahaja bahawa class Form1, mengumpulkan beberapa kode. Kata kunci “public”, bermaksud class ini boleh diakses dari luar namespacenya dan kata kunci “partial” bermaksud source code ini hanyalah sebahagian daripada Form1 yang mana sebahagian lagi dijana oleh form builder.

Baris “public Form1()” mengisytiharkan satu method/subroutine/function. subroutine, secara ringkasnya adalah sekumpulan kode-kode. Dalam kes ini, hanya ada satu baris kod iaitu “InitializeComponent()”. Kode ini bermaksud, jalankan method yang bernama “InitializeComponent” yang diisytiharkan oleh form builder di partial yang lain. Lihat bahawa method “Form1” mempunyai nama yang sama dengan class tersebut. Method yang mempuntai nama yang sama dengan class adalah constructor kepada class itu. Ini bermakna method “Form1” merupakan constructor kepada class “Form1”. Apabila sesuatu object diwujudkan, constructor kepada class object tersebut dijalankan untuk membina object tersebut.

Sekarang ini, kode hanya menyusun control kepada form itu sahaja. Mari kita ubahnya untuk buat sesuatu yang lebih menarik. Dibawah baris “InitializeComponent”, tulis “pa”.

Satu dropdown menu akan muncul, menyenaraikan perkataan-perkataan yang sesuai diletakkan. Antara perkataan tersebut adalah “panel1”. Ingat bahawa nama panel yang kita letak di form builder adalah “panel1”. Form builder akan secara automatiknya, membuat satu global variable yang sama nama dengan panel tersebut di partial yang lain untuk memudahkan kita mengakses control tersebut. Atas perkataan “panel1”, ada perkataan “Panel”. Panel adalah nama class yang “define” satu panel dalam form. Nama penuhnya adalah System.Windows.Form.Panel. Tetapi, disebabkan kita telah meletakkan baris “using System.Windows.Form”, perkataan “Panel” sudah mencukupi. Secara konvensional, nama class dimulakan dengan huruf besar manakala nama property dimulakan dengan huruf kecil. Kamu boleh menggunakan Panel untuk meletakkan panel secara manual, namun, bukan itu yang kita akan buat pada hari ini. Rujukan lengkap tentang class apa yang ada dalam .Net Framework boleh didapati di sini. Sekarang, tekan Tab dan ia akan lengkapkan perkataan tersebut. Kemudian taip lagi “.pa” dihadapannya.

Sekarang satu lagi dropdown menu muncul tetapi dengan perkataan yang lain. Apabila kita menggunakan symbol ‘.’, kita menyatakan bahawa kita sedang memilih property/attribute/member object tersebut. Dan itu yang kita akan lakukan. Kita akan lihat nanti bagaimana untuk buat property kita sendiri. Sekarang, tekan tab untuk auto-complete perkataan Paint. Kemudian, tambah “+=” dihadapannya.

Property “Paint” ialah sejenis delegate satu lagi feature C# yang agak advance. Untuk kali ini, kita tidak belajar secara mendalam tentang delegate. Kita hanya akan menggunakannya. Secara ringkasnya, delegate merupakan satu system event-handler yang ada dalam C# yang membolehkan kita untuk menjalankan sesuatu method apabila satu event berlaku. Dalam kes ini, event “Paint” berlaku apabila control tersebut dilukis. Tekat Tab dua kali dan ia akan menjana satu method.

method yang baru dijanakan sebentar tadi dinamakan “panel1_Paint”. Kata kunci “void” bermaksud method ini tidak me-“return” sebarang data. Apa yang dimaksudkan dengan “return” itu tidak akan kita bincangkan pada hari ini memandangkan setiap method yang kita buat pada hari ini adalah method yang tidak me-return sebarang variable. Method ini juga mempunyai parameter “object sender” dan “PaintEventArgs e”. parameter adalah nilai/variables yang boleh diberi kepada method ini. variable atau dalam bahasa melayunya pembolehubah merupakan tempat untuk meletakkan nilai atau object sepertimana juga property yang mana kemungkinan besar merupakan public variable. Kita akan lihat bagaimana untuk mengisytihar variable sebentar nanti. Secara defaultnya method ini mengandungi satu baris kode yakni “throw new NotImplementedException()”. Secara ringkasnya, kode ini akan menghasilkan satu exception. exception merupakan “simbol masalah yang terkawal”. Dalam kes ini, ia akan menghasilkan exception kerana kita tidak memberitahu apa yang nak dibuat. Disamping itu, lihat bahawa setiap statement diakhiri dengan semicolon ‘;’. Dalam C#, setiap statement diakhiri dengan semicolon. Sekarang, padam baris exception tersebut dan salin kode dibawah.

Baris pertama “Graphics g=e.Graphics;” menunjukkan cara untuk mengishtihar dan meletakkan object kepada satu variable baru yang bernama ‘g’. C# ialah sejenis bahasa programming yang ‘strongly type’. Ini bermakna, setiap variable perlu ada ‘jenis/type’ yang jelas. ‘jenis/type’ dalam kontext ini adalah satu nama class ataupun jenis yang sudah terbina dalam C#. Dalam kes ini ‘g’ adalah satu variable berjenis ‘Graphics’. ‘Graphics’ ialah satu class yang membolehkan kita untuk melukis di atas sesuatu. Nama penuhnya adalah ‘System.Drawing.Graphics’. Simbol ‘=’ adalah simbol ‘assignment/peletak’, yang bermakna nilai di sebelah kanan simbol akan disalin ke variable di sebelah kiri simbol. Secara ringkasnya, kita sedang membuat satu variable bernama ‘g’ yang berjenis ‘Graphics’ dengan nilai daripada ‘e.Graphics’ yang mana ‘Graphics’ di sini adalah property kepada variable ‘e’ yang merupakan parameter kepada method ‘panel1_Paint’.

Di baris seterusnya kita sedang call/memanggil method ‘DrawString’ daripada variable g. Lihat di tooltip ada perkataan ‘1 of 6’ dan anak panah di tepinya. Jika kamu click anak panah tersebut, kamu akan lihat method DrawString boleh menerima lebih dari satu jenis set parameter dengan susunan jenis yang berbeza. Dalam kes ini ada 6 set. Ini dipanggil method/function overloading. Sebenarnya dalam class Graphics, ada 6 method yang mempunyai nama yang sama yakni “DrawString” tetapi setiap satu mempunyai set parameter yang berlainan. Compiler akan secara automatiknya memilih method yang betul bergantung kepada variable yang diberi semasa method call.

Dalam kes ini kita akan menggunakan overload yang ketiga. Isikan method call ini mengikut screenshot dibawah. Kamu tidak perlu untuk click anak panah untuk memilih function overload. Tool tip tersebut hanyalah alat bantuan semata-mata.

Oh ya! semasa kamu sedang mengisi parameter ‘brush’, tooltip akan keluarkan beberapa kata kunci yang bermula dengan ‘Bru’ dan berkaitan dengannya. Buat masa ini, saya cadangkan kamu gunakan sahaja “Brushes” yang mengandungi koleksi ‘Brush’ yang sudah sedia ada.

Apabila kamu sudah melengkapkan kod tersebut, jalankan program dengan menekan butang “Start debugging” atau menggunakan shortcut F5. Kamu patut lihat satu window seperti di bawah.

+
Begitulah caranya programmer melukis diatas panel. Perhatikan bahawa nilai x adalah 5, manakala nilai y adalah 50. Dalam kebanyakan programming toolkit, origin (x=0,y=0) terletak di atas kiri screen/panel dan nilai positif y menghala ke bawah. Ini bermakna apabila nilai y meningkat, lukisan akan begerak ke bawah, dan apabila nilai x meningkat, lukisan akan bergerak ke kanan. 
Sekarang, mari kita buat sesuatu yang lebih menarik. Ishtiharkan dua global variable seperti di bawah.

global variable secara ringkasnya adalah variable yang diishtiharkan di luar class method. Bezanya adalah global variable boleh diakses dari mana-mana class method, dan sebarang perubahan terhadap global variable akan dikekalkan sepanjang hayat object tersebut. Berbanding global variable, local variable (variable didalam method) hanya boleh diakses didalam method tersebut dan diishtiharkan semula setiap kali method itu digunakan. “int” pula bermaksud “integer” iaitu salah satu type/jenis yang sudah terbina dalam C#, digunakan untuk menyimpan nombor bulat.

Seterusnya, kita tambah lagi satu handler untuk “MouseMove” event seperti mana yang kita lakukan dengan “Paint” event.

Dalam mouse handler, gunakan kode seperti di atas.

Kemudian, ubah paint handler kepada kod diatas.

So, apabila tetikus bergerak di atas panel, ia patut tukar variable “myx” dan “myy” kepada kedudukan tetikus dan apabila panel dilukis, ia akan melukis dua garisan yang akan bertemu di kedudukan tetikus. Sekarang, mari kita cuba program itu, tekan F5 dan kamu patut lihat window seperti di bawah.

Cuba gerakkan tetikus di atas panel tersebut, adakah garisan itu bergerak? Kenape? Tips: Minimize, kemudian maximize window tersebut. Sekarang garisan itu patut berada di kedudukan yang berbeza.

Masalahnya di sini adalah, event paint hanya berlaku ketika window perlu dilukis semula. Contohnya ketika window tersebut didedahkan daripada bawah window yang lain atau dalam kes ini, apabila window sedang di maximize. Sistem Operasi (selalunya) tidak akan ingat bagaimana rupa sesuatu window apabila ia di maximize, jadi ia minta program tersebut untuk melukis dirinya sendiri. Dan pada waktu itulah paint handler digunakan, yang mana ketika itu, ia akan melukis garisan bergantung kepada kedudukan tetikus yang disimpan. Untuk membuatkan garisan itu bergerak mengikut tetikus, kita perlu invalidate(bermaksud tidak sah) panel apabila mouse bergerak.

Untuk invalidate panel tersebut, kita gunakan method Invalidate(). Secara ringkasnya, method ini akan menandakan panel itu sebagai tidak sah, jadi ia perlu dilukis semula. Ubah kode di mouse handler seperti di atas dan jalankan program.

Sekarang kamu patut lihat garisan itu bergerak mengikut tetikus. Sekarang, mari kita buat sesuatu yang lebih menarik. Ubah paint handler anda seperti di bawah.

Baris “int phase=(DateTime.Now.Milisecond/100)%10” secara ringkasnya bermaksud, dapatkan masa sekarang dalam milisaat, bahagi dengan 100 dan kemudian dapatkan baki jika dibahagi lagi dengan 10. Hasilnya ia akan menjadi nombor diantara 0 hingga 9 yang meningkat 1 setiap 100 milisaat. Bagaimana ia berguna akan ditunjuk sebentar nanti.

Kata kunci ‘while’ akan menyebabkan komputer menjalankan kode didalam curly bracket(loop/block) selagi expression “i<5" itu benar. Expressi "i<5" dikira benar selagi nilai dalam variable "i" itu kurang dari 5. Disebabkan "i" asalnya 0, dan ianya ditambah 1 setiap kali block ini dijalankan, code didalam block ini akan dijalankan sebanyak 5 kali. Didalam block tersebut, kita mengira ‘ciclesize’ iaitu 10 x nombor loop + phase. Kemudian kita lukis bulatan di tengah-tengah panel. Apa yang ia lakukan? Well, mari kita jalankannya dan program kamu patut jadi sebegini.

Lima bulatan didalam bulatan yang lain. Jika kamu terus menggerakkan tetikus kamu, kamu akan perasan yang ianya sebenarnya satu animasi. Tetapi untuk ia bergerak, kamu perlu terus menggerakkan tetikus kamu supaya ia akan invalidate panel tersebut supaya panel itu akan dilukis semula. Untuk membolehkan ia bergerak tanpa menggunakan mouse, kita boleh gunalan Timer.

Ubah kod anda menjadi seperti yang diatas. Event “Tick” pemasa, akan berlaku setiap 100 milisaat jika di set seperti yang diatas. Di dalam handler, ia invalidate panel1. Disebabkan panel1 invalidate setiap 100 milisaat, kita tidak perlu invalidatenye dalam MouseMove handler. Tetapi tidak ada salahnya jika kita biarkan sahaja kod tersebut. Sekarang, bulatan itu akan terus bergerak walaupun tetikus tidak bergerak.

Sekarang, cuba anda tukar kod di paint handler seperti yang diatas. Cuba teka apa yang berlaku?

Sekarang bulatan itu akan mengikut tetikus! Dua perubahan ringkas dan ia lakukan sesuatu yang nampak komplex. Membuat program itu senang, tetapi buat program bagus yang senang diubah itu susah.

Kamu perasan tak yang lukisan ini macam berkelip/flicker. Itu kerana, lukisan ini ditunjukkan walaupun ia belum siap. Ada technique ringkas yang boleh mengatasi masalah ini. Ia dipanggil double buffering. Secara ringkasnya ia bermaksud, kita lukis diatas satu image(buffer), kemudian kita lukis image tersebut diatas panel. Panel ada double buffering sedia ada yang tidak dihidupkan. Namun, untuk set double buffering, kita perlu subclass Panel terlebih dahulu, sesuatu yang agak mengelirukan buat masa ini. Jadi kita akan buat implementasi double buffering kita sendiri.

Secara ringkasnya, kita akan buat satu Bitmap(sejenis Image) yang sama saiz dengan panel, kemudian kita lukis atas Bitmap itu, dan kemudian kita lukis Bitmap itu diatas panel. Jalankan program ini, dan kamu patut lihat pengurangan flicker yang agak drastik. Dan apa yang kita lakukan cumalah tambah dua baris dan ubah satu. Disebabkan kita simpan e.Graphics di variable g terlebih dahulu dan kemudian lukis menggunakan g dan bukannya secara terus dari e.Graphics, kita hanya perlu ubah ‘g’ dan segalanya akan berubah.

So, apa yang kita belajar hari ini? Ada banyak benda yang boleh dibuat dalam program. Buat program itu senang, tapi buat program yang bagus itu susah. So, sekarang ni, apa kata kamu try ubah kod kamu mengikut apa yang kamu suka. Selamat berjaya, good luck dan Assalamualaikum.

Leave a Reply

Your email address will not be published.