Jadi, ada sesuatu yang menggangguku tentang dukungan async baru di C # 5:
Pengguna menekan tombol yang memulai operasi async. Panggilan segera kembali dan pompa pesan mulai berjalan lagi - itulah intinya.
Jadi pengguna dapat menekan tombol lagi - menyebabkan masuknya kembali. Bagaimana jika ini masalah?
Dalam demo yang saya lihat, mereka menonaktifkan tombol sebelum await
panggilan dan mengaktifkannya lagi setelahnya. Bagi saya ini sepertinya solusi yang sangat rapuh dalam aplikasi dunia nyata.
Haruskah kita membuat kode semacam mesin negara yang menentukan kontrol mana yang harus dinonaktifkan untuk serangkaian operasi yang dijalankan? Atau ada cara yang lebih baik?
Saya tergoda untuk hanya menampilkan dialog modal selama operasi, tetapi ini terasa seperti menggunakan palu godam.
Adakah yang punya ide cemerlang?
EDIT:
Saya pikir menonaktifkan kontrol yang tidak boleh digunakan saat operasi sedang berjalan rapuh karena saya pikir itu akan cepat menjadi kompleks ketika Anda memiliki jendela dengan banyak kontrol di atasnya. Saya suka menjaga hal-hal sederhana karena mengurangi kemungkinan bug, baik selama pengkodean awal dan pemeliharaan selanjutnya.
Bagaimana jika ada kumpulan kontrol yang harus dinonaktifkan untuk operasi tertentu? Dan bagaimana jika beberapa operasi berjalan secara bersamaan?
sumber
Jawaban:
Mari kita mulai dengan mencatat bahwa ini sudah menjadi masalah, bahkan tanpa dukungan async dalam bahasa tersebut. Banyak hal yang dapat menyebabkan pesan terhapus saat Anda menangani suatu acara. Anda sudah harus kode membela diri untuk situasi ini; fitur bahasa baru hanya membuatnya lebih jelas , yaitu kebaikan.
Sumber paling umum dari loop pesan yang berjalan selama acara yang menyebabkan pemanggilan kembali yang tidak diinginkan adalah
DoEvents
. Hal yang menyenangkan tentangawait
sebagai lawanDoEvents
adalah yangawait
membuatnya lebih mungkin bahwa tugas-tugas baru tidak akan "kelaparan" tugas yang sedang berjalan.DoEvents
memompa loop pesan dan kemudian secara sinkron memanggil pengendali event re-entrant, sedangkanawait
biasanya enqueues tugas baru sehingga akan berjalan di beberapa titik di masa depan.Anda dapat mengelola kompleksitas itu dengan cara yang sama seperti Anda mengelola kompleksitas lain dalam bahasa OO: Anda mengabstraksi mekanisme menjadi kelas, dan membuat kelas bertanggung jawab untuk menerapkan kebijakan dengan benar . (Cobalah untuk menjaga mekanisme secara logis berbeda dari kebijakan; harus mungkin untuk mengubah kebijakan tanpa terlalu banyak mempermainkan mekanisme.)
Jika Anda memiliki formulir dengan banyak kontrol di atasnya yang berinteraksi dengan cara yang menarik, maka Anda hidup dalam dunia yang rumit dari buatan Anda sendiri . Anda harus menulis kode untuk mengelola kerumitan itu; jika Anda tidak menyukainya maka sederhanakan formulirnya sehingga tidak terlalu rumit.
Pengguna akan membencinya.
sumber
Mengapa Anda berpikir menonaktifkan tombol sebelum
await
dan kemudian mengaktifkannya kembali ketika panggilan selesai rapuh? Apakah karena Anda khawatir tombol itu tidak akan pernah diaktifkan kembali?Nah, jika panggilan tidak kembali maka Anda tidak dapat membuat panggilan lagi, jadi sepertinya ini adalah perilaku yang Anda inginkan. Ini menunjukkan status kesalahan yang harus Anda perbaiki. Jika panggilan async Anda habis maka ini masih bisa meninggalkan aplikasi Anda dalam keadaan tak tentu - lagi yang memiliki tombol diaktifkan bisa berbahaya.
Satu-satunya saat ini mungkin menjadi berantakan adalah jika ada beberapa operasi yang mempengaruhi keadaan tombol, tapi saya pikir situasi itu harus sangat jarang.
sumber
Saya tidak yakin apakah ini berlaku untuk Anda karena saya biasanya menggunakan WPF, tapi saya melihat Anda mereferensikan
ViewModel
jadi mungkin.Alih-alih menonaktifkan tombol, atur
IsLoading
bendera ketrue
. Kemudian setiap elemen UI yang ingin Anda nonaktifkan dapat diikat ke flag. Hasil akhirnya adalah menjadi tugas UI untuk memilah status diaktifkan sendiri, bukan Logika Bisnis Anda.Selain itu, sebagian besar tombol di WPF terikat ke
ICommand
, dan biasanya saya mengaturICommand.CanExecute
sama dengan!IsLoading
, sehingga secara otomatis mencegah perintah dari mengeksekusi ketika IsLoading sama dengan true (juga menonaktifkan tombol untuk saya)sumber
Tidak ada yang rapuh tentang ini, dan itulah yang diharapkan pengguna. Jika pengguna perlu menunggu sebelum menekan tombol lagi maka buat mereka menunggu.
Saya dapat melihat bagaimana skenario kontrol tertentu dapat memutuskan kontrol mana yang akan dinonaktifkan / diaktifkan pada satu waktu yang cukup kompleks, tetapi apakah mengejutkan bahwa mengelola UI yang rumit akan menjadi rumit? Jika Anda memiliki terlalu banyak efek samping yang menakutkan dari kontrol tertentu, Anda selalu dapat menonaktifkan seluruh formulir dan melemparkan "memuat" whirly-manggung atas semuanya. Jika ini adalah kasus pada setiap kontrol tunggal, maka UI Anda mungkin tidak dirancang dengan sangat baik di tempat pertama (kopling ketat, pengelompokan kontrol yang buruk, dll.).
sumber
Menonaktifkan widget adalah opsi yang paling kompleks dan rawan kesalahan. Anda menonaktifkannya di handler sebelum memulai operasi async dan Anda mengaktifkannya kembali di akhir operasi. Jadi, disable + enable untuk setiap kontrol disimpan lokal untuk menangani kontrol itu.
Anda bahkan dapat membuat pembungkus, yang akan mengambil delegasi dan kontrol (atau lebih dari mereka), menonaktifkan kontrol dan menjalankan sesuatu secara tidak sinkron, yang akan melakukan delegasi dan daripada mengaktifkan kembali kontrol. Itu harus menangani pengecualian (lakukan mengaktifkan akhirnya), sehingga masih berfungsi dengan andal jika operasi gagal. Dan Anda dapat menggabungkannya dengan menambahkan pesan di bilah status, sehingga pengguna tahu apa yang dia tunggu.
Anda mungkin ingin menambahkan beberapa logika untuk menghitung penonaktifan, sehingga sistem tetap berfungsi jika Anda memiliki dua operasi, yang harus menonaktifkan operasi yang ketiga, tetapi tidak saling terpisah. Anda menonaktifkan kontrol dua kali, sehingga akan diaktifkan kembali hanya jika Anda mengaktifkannya dua kali lagi. Itu harus mencakup sebagian besar kasus sambil menjaga kasus terpisah sebanyak yang Anda bisa.
Di sisi lain apapun seperti mesin negara akan menjadi kompleks. Dalam pengalaman saya, semua mesin negara yang pernah saya lihat sulit untuk dirawat dan mesin negara Anda harus mencakup seluruh dialog, mengikat segalanya untuk semuanya.
sumber
Meskipun Jan Hudec dan Rachel telah mengungkapkan ide-ide terkait, saya akan menyarankan menggunakan sesuatu seperti arsitektur Model-View - ungkapkan keadaan bentuk dalam suatu objek dan ikat elemen-elemen bentuk ke keadaan itu. Ini akan menonaktifkan tombol dengan cara yang dapat menskala untuk skenario yang lebih kompleks.
sumber