Apakah ada semacam strategi sistematis untuk merancang dan mengimplementasikan GUI?

18

Saya menggunakan Visual Studio untuk membuat aplikasi GUI di C #. Toolbox berfungsi sebagai palet komponen yang bagus yang memungkinkan saya untuk dengan mudah menarik dan melepas tombol dan elemen lainnya (untuk kejelasan saya akan mengatakan tombol setiap kali saya maksudkan "kontrol") ke formulir saya, yang membuat bentuk statis cukup mudah dilakukan. Namun, saya mengalami dua masalah:

  • Membuat tombol di tempat pertama adalah banyak pekerjaan. Ketika saya memiliki bentuk yang tidak statis (mis. Tombol atau kontrol lain dibuat pada saat run time sesuai dengan apa yang dilakukan pengguna) Saya tidak bisa menggunakan palet sama sekali. Sebaliknya saya harus membuat setiap tombol secara manual, dengan memanggil konstruktor dalam metode apa pun yang saya gunakan, dan kemudian secara manual menginisialisasi dengan menentukan tinggi tombol, lebar, posisi, label, event handler dan sebagainya. Ini sangat membosankan karena saya harus menebak semua parameter kosmetik ini tanpa bisa melihat seperti apa bentuknya, dan juga menghasilkan banyak baris kode berulang untuk setiap tombol.
  • Membuat tombol melakukan sesuatu juga banyak pekerjaan. Berurusan dengan acara dalam aplikasi fitur lengkap adalah rasa sakit yang sangat besar. Satu-satunya cara saya tahu bagaimana melakukan ini adalah dengan memilih tombol, pergi ke tab acara di propertinya, klik OnClickacara sehingga menghasilkan acara dalam Formkode, lalu isi isi acara. Karena saya ingin memisahkan logika dan presentasi, semua penangan acara saya akhirnya menjadi panggilan satu baris ke fungsi logika bisnis yang sesuai. Tetapi menggunakan ini untuk banyak tombol (misalnya, bayangkan jumlah tombol yang ada dalam aplikasi seperti MS Word) mencemari kode saya Formdengan puluhan metode event handler boilerplate dan sulit untuk mempertahankannya.

Karena itu, setiap program GUI yang lebih rumit daripada Hello World sangat tidak praktis untuk benar-benar membuat saya. Untuk lebih jelasnya, saya tidak punya masalah apa pun berurusan dengan kompleksitas dalam program yang saya tulis yang memiliki UI minimal - Saya merasa seperti saya dapat menggunakan OOP dengan tingkat kompetensi yang adil untuk menyusun dengan rapi kode logika bisnis saya. Tetapi ketika mengembangkan GUI, saya mandek. Tampaknya sangat membosankan sehingga saya merasa seperti saya menemukan kembali roda, dan ada sebuah buku di luar sana yang menjelaskan bagaimana melakukan GUI dengan benar yang belum saya baca.

Apakah saya melewatkan sesuatu? Atau apakah semua pengembang C # hanya menerima daftar pengendali acara berulang dan kode pembuatan tombol yang tak ada habisnya?


Sebagai petunjuk (semoga bermanfaat), saya berharap jawaban yang baik akan berbicara tentang:

  • Menggunakan teknik OOP (seperti pola pabrik) untuk menyederhanakan pembuatan tombol berulang
  • Menggabungkan banyak penangan peristiwa ke dalam metode tunggal yang memeriksa Senderuntuk mengetahui tombol mana yang memanggilnya, dan berperilaku sesuai
  • XAML dan menggunakan WPF, bukan Windows Forms

Anda tidak harus menyebutkan semua ini, tentu saja. Hanya tebakan terbaik saya untuk jawaban seperti apa yang saya cari.

Hebat
sumber
Seperti yang saya lihat ya Factory akan menjadi pola yang baik tetapi saya melihat lebih banyak pola Model-View-Controller dengan Perintah yang terhubung ke kontrol, bukan acara secara langsung. WPF biasanya MVVM tetapi MVC sangat memungkinkan. Saat ini kami memiliki perangkat lunak besar di WPF menggunakan MVC sebesar 90%. Semua perintah diteruskan ke controller dan dia menangani semua
Franck
Tidak bisakah Anda membuat formulir dinamis dengan semua tombol yang mungkin dengan editor GUI, dan hanya membuat hal-hal yang tidak Anda perlukan tidak terlihat secara dinamis? Kedengarannya seperti kurang bekerja.
Hans-Peter Störr
@ hstoerr Tapi itu akan sulit untuk mendapatkan tata letak untuk bekerja. Banyak tombol akan tumpang tindih.
Hebat

Jawaban:

22

Ada beberapa metodologi yang telah berkembang selama bertahun-tahun untuk menangani masalah-masalah yang telah Anda sebutkan ini, yang saya setujui, dua masalah utama yang harus ditangani kerangka kerja UI dalam beberapa tahun terakhir. Berasal dari latar belakang WPF, ini didekati sebagai berikut:

Desain deklaratif, bukan keharusan

Saat Anda mendeskripsikan kode dengan susah payah untuk memberi contoh kontrol dan mengatur propertinya, Anda menggambarkan model desain UI yang sangat penting. Bahkan dengan perancang WinForms, Anda hanya menggunakan pembungkus atas model itu - buka file Form1.Designer.cs dan Anda melihat semua kode itu berada di sana.

Dengan WPF dan XAML - dan model serupa dalam kerangka kerja lain, tentu saja, dari HTML dan seterusnya - Anda diharapkan untuk menggambarkan tata letak Anda, dan biarkan kerangka kerja melakukan hal yang berat dalam mengimplementasikannya. Anda menggunakan kontrol yang lebih cerdas seperti Panel (Kotak, atau WrapPanel, dll) untuk menggambarkan hubungan antara elemen UI, tetapi harapannya adalah Anda tidak memposisikannya secara manual. Konsep fleksibel seperti kontrol berulang - seperti ASP.NET's Repeater atau WPF's ItemsControl - membantu Anda membuat UI penskalaan dinamis tanpa menulis kode berulang, memungkinkan entitas data yang tumbuh secara dinamis untuk diwakili secara dinamis sebagai kontrol.

DataTemplates WPF memungkinkan Anda untuk mendefinisikan - lagi, secara deklaratif - nugget kecil dari kebaikan UI dan mencocokkannya dengan data Anda; misalnya, daftar objek data Pelanggan mungkin terikat ke ItemsControl, dan templat data berbeda dipanggil tergantung pada apakah dia karyawan biasa (menggunakan templat baris kisi standar dengan nama dan alamat) atau Pelanggan Utama, sedangkan templat berbeda , dengan gambar dan tombol yang mudah diakses ditampilkan. Sekali lagi, tanpa menulis kode di jendela spesifik , hanya kontrol yang lebih cerdas yang menyadari konteks data mereka, dan dengan demikian memungkinkan Anda untuk hanya mengikatnya ke data Anda dan membiarkan mereka melakukan operasi yang relevan.

Penjilidan Data dan Pemisahan Perintah

Menyentuh pengikatan data, penyatuan data WPF (dan, sekali lagi, dalam kerangka kerja lain juga, seperti AngularJS) memungkinkan Anda untuk menyatakan maksud Anda dengan menautkan kontrol (katakanlah, kotak teks) dengan entitas data (katakanlah, Nama Pelanggan) dan biarkan Kerangka kerja menangani pipa ledeng. Logika ditangani dengan cara yang sama. Alih-alih secara manual memasang kode di belakang event-handler ke logika bisnis berbasis Controller, Anda menggunakan mekanisme Binding Data untuk menautkan perilaku pengontrol (katakanlah, properti Command tombol) ke objek Command yang mewakili nugget aktivitas.

Ini memungkinkan Perintah ini untuk dibagikan di antara windows tanpa menulis ulang penangan acara setiap saat.

Abstraksi Tingkat Tinggi

Kedua solusi ini untuk kedua masalah Anda menunjukkan perpindahan ke tingkat abstraksi yang lebih tinggi daripada paradigma Windows-form yang didorong oleh peristiwa yang menurut Anda melelahkan.

Idenya adalah bahwa Anda tidak ingin mendefinisikan, dalam kode, setiap properti tunggal yang memiliki kontrol, dan setiap perilaku mulai dari klik tombol dan seterusnya. Anda ingin kontrol dan kerangka kerja yang mendasari untuk melakukan lebih banyak pekerjaan untuk Anda dan memungkinkan Anda untuk berpikir dalam konsep-konsep yang lebih abstrak dari Data Binding (yang ada di WinForms, tetapi sama sekali tidak berguna seperti di WPF) dan pola Command untuk menentukan tautan antara UI dan perilaku yang tidak mengharuskan turun ke logam.

The MVVM pola pendekatan Microsoft untuk paradigma ini. Saya sarankan membaca itu.

Ini adalah contoh kasar tentang bagaimana model ini akan terlihat dan bagaimana itu akan menghemat waktu dan garis kode. Ini tidak dapat dikompilasi, tetapi ini adalah pseudo-WPF. :)

Di suatu tempat di sumber daya aplikasi Anda, Anda mendefinisikan Templat Data:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

Sekarang, Anda menautkan layar utama Anda ke ViewModel, kelas yang mengekspos data Anda. Katakanlah, kumpulan Pelanggan (hanya a List<Customer>) dan objek Perintah (sekali lagi, properti publik tipe sederhana ICommand). Tautan ini memungkinkan pengikatan:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

dan UI:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

Dan itu saja. Sintaks ListBox akan mengambil daftar Pelanggan dari ViewModel dan mencoba untuk merender mereka ke UI. Karena dua DataTemplates yang kami definisikan sebelumnya, ia akan mengimpor templat yang relevan (berdasarkan pada DataType, dengan anggapan PrimeCustomer mewarisi dari Pelanggan) dan meletakkannya sebagai isi ListBox. Tidak ada perulangan, tidak ada generasi dinamis kontrol melalui kode.

Button, juga, telah memiliki sintaksis yang sudah ada sebelumnya untuk menghubungkan perilakunya dengan implementasi ICommand, yang mungkin tahu untuk memperbarui Customersproperti - mendorong kerangka kerja pengikatan data untuk memperbarui UI lagi, secara otomatis.

Saya telah mengambil beberapa jalan pintas di sini, tentu saja, tetapi ini adalah intinya.

Avner Shahar-Kashtan
sumber
5

Pertama, saya merekomendasikan seri artikel ini: http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/ - tidak membahas masalah detail dengan kode tombol Anda, tetapi memberi Anda strategi sistematis untuk merancang dan mengimplementasikan kerangka kerja aplikasi Anda sendiri, terutama untuk aplikasi GUI, menunjukkan kepada Anda cara menerapkan MVC, MVP, MVVM ke aplikasi Forms khas Anda.

Mengatasi pertanyaan Anda tentang "berurusan dengan acara": jika Anda memiliki banyak formulir dengan tombol yang bekerja dengan cara yang sama, mungkin akan terbayar untuk mengimplementasikan penugasan event handler dalam "kerangka aplikasi" Anda sendiri. Alih-alih menugaskan peristiwa klik menggunakan desainer GUI, buat konvensi penamaan bagaimana penangan "klik" harus diberi nama untuk tombol nama tertentu. Misalnya - untuk tombol MyNiftyButton_ClickHandleruntuk tombol MyNiftyButton. Maka tidak terlalu sulit untuk menulis rutin yang dapat digunakan kembali (menggunakan refleksi) yang beralih di atas semua tombol formulir, periksa apakah formulir tersebut berisi penangan klik terkait dan tetapkan penangan ke acara secara otomatis. Lihat di sini untuk contoh.

Dan jika Anda hanya ingin menggunakan satu event handler untuk banyak tombol, itu juga mungkin. Karena setiap pengendali peristiwa dapat meloloskan pengirim, dan pengirim selalu merupakan tombol yang ditekan, Anda dapat mengirim ke kode bisnis dengan memanfaatkan properti "Nama" dari setiap tombol.

Untuk pembuatan tombol yang dinamis (atau kontrol lain): Anda dapat membuat tombol atau kontrol lain dengan perancang pada formulir yang tidak digunakan hanya untuk tujuan menggunakan templat . Kemudian, Anda menggunakan refleksi (lihat di sini untuk contoh ) untuk mengkloning kontrol template dan hanya mengubah properti seperti lokasi atau ukuran. Itu mungkin akan jauh lebih sederhana daripada menginisialisasi dua atau tiga lusin properti secara manual dalam kode.

Saya menulis ini di atas dengan mengingat Winforms (sama seperti Anda, saya kira), tetapi prinsip-prinsip umum harus berlaku untuk lingkungan GUI lain seperti WPF dengan kemampuan yang sama.

Doc Brown
sumber
Memang mungkin dilakukan di WPF dan itu bahkan jauh lebih mudah. Anda cukup mengisi daftar perintah (fungsi khusus pra-kode) dan mengatur sumber pada jendela Anda. Anda membuat templat visual sederhana yang bagus dan yang perlu Anda lakukan hanyalah mengisi koleksi dengan perintah yang Anda inginkan dan pengikatan WPF yang hebat akan mengurus sisanya untuk Anda secara visual.
Franck