Apakah Application.DoEvents()
bisa digunakan dalam C #?
Apakah fungsi ini merupakan cara untuk memungkinkan GUI untuk mengejar ketinggalan dengan sisa aplikasi, sama seperti VB6 DoEvents
?
Apakah Application.DoEvents()
bisa digunakan dalam C #?
Apakah fungsi ini merupakan cara untuk memungkinkan GUI untuk mengejar ketinggalan dengan sisa aplikasi, sama seperti VB6 DoEvents
?
Jawaban:
Hmya, mistik abadi DoEvents (). Sudah ada banyak reaksi terhadapnya, tetapi tidak ada yang benar-benar menjelaskan mengapa itu "buruk". Jenis kebijaksanaan yang sama dengan "jangan bermutasi sebuah struct". Erm, mengapa runtime dan bahasa mendukung mutating struct jika itu sangat buruk? Alasan yang sama: Anda menembak diri sendiri di kaki jika Anda tidak melakukannya dengan benar. Dengan mudah. Dan melakukannya dengan benar membutuhkan mengetahui persis apa yang dilakukannya, yang dalam kasus DoEvents () jelas tidak mudah untuk diraih.
Langsung saja: hampir semua program Windows Forms sebenarnya berisi panggilan ke DoEvents (). Ini disamarkan dengan cerdik, namun dengan nama yang berbeda: ShowDialog (). DoEvents () yang memungkinkan dialog menjadi modal tanpa membekukan sisa jendela dalam aplikasi.
Sebagian besar programmer ingin menggunakan DoEvents untuk menghentikan antarmuka pengguna agar tidak membeku ketika mereka menulis loop modal mereka sendiri. Tentu saja itu; itu mengirim pesan Windows dan mendapatkan permintaan cat dikirim. Namun masalahnya adalah tidak selektif. Ini tidak hanya mengirim pesan cat, tapi juga mengirim yang lainnya.
Dan ada satu set pemberitahuan yang menyebabkan masalah. Mereka datang sekitar 3 kaki di depan monitor. Misalnya pengguna dapat menutup jendela utama saat loop yang memanggil DoEvents () sedang berjalan. Itu berhasil, antarmuka pengguna hilang. Tetapi kode Anda tidak berhenti, itu masih mengeksekusi loop. Itu buruk. Sangat sangat buruk.
Masih ada lagi: Pengguna dapat mengklik item menu yang sama atau tombol yang menyebabkan loop yang sama untuk memulai. Sekarang Anda memiliki dua loop bersarang yang mengeksekusi DoEvents (), loop sebelumnya ditangguhkan dan loop baru mulai dari awal. Itu bisa berhasil, tetapi kemungkinannya kecil. Terutama ketika loop bersarang berakhir dan yang ditangguhkan dilanjutkan, mencoba untuk menyelesaikan pekerjaan yang sudah selesai. Jika itu tidak mengebom dengan pengecualian maka pasti data tersebut diacak semua ke neraka.
Kembali ke ShowDialog (). Itu mengeksekusi DoEvents (), tetapi perhatikan bahwa ia melakukan sesuatu yang lain. Ini menonaktifkan semua jendela dalam aplikasi , selain dialog. Sekarang masalah 3 kaki diselesaikan, pengguna tidak dapat melakukan apa pun untuk mengacaukan logika. Mode kegagalan tutup-jendela dan mulai-pekerjaan-lagi terpecahkan. Atau dengan kata lain, tidak ada cara bagi pengguna untuk membuat kode program Anda dalam urutan yang berbeda. Itu akan dieksekusi dapat diprediksi, sama seperti itu ketika Anda menguji kode Anda. Itu membuat dialog sangat menjengkelkan; siapa yang tidak suka dialog aktif dan tidak bisa menyalin dan menempelkan sesuatu dari jendela lain? Tapi itu harganya.
Yang diperlukan untuk menggunakan DoEvents dengan aman dalam kode Anda. Menyetel properti Diaktifkan dari semua formulir Anda ke false adalah cara cepat dan efisien untuk menghindari masalah. Tentu saja, tidak ada programmer yang benar-benar suka melakukan ini. Dan tidak. Itu sebabnya Anda tidak harus menggunakan DoEvents (). Anda harus menggunakan utas. Meskipun mereka memberi Anda persenjataan lengkap cara untuk menembak kaki Anda dengan cara yang penuh warna dan tidak bisa dipahami. Tetapi dengan keuntungan bahwa Anda hanya menembak kaki Anda sendiri; itu tidak akan (biasanya) membiarkan pengguna menembak miliknya.
Versi C # dan VB.NET selanjutnya akan memberikan senjata yang berbeda dengan kata kunci tunggu dan async yang baru. Terinspirasi sebagian kecil oleh masalah yang disebabkan oleh DoEvents dan utas tetapi sebagian besar oleh desain API WinRT yang mengharuskan Anda untuk menjaga UI Anda diperbarui saat operasi asinkron berlangsung. Suka membaca dari file.
BackgroundWorker
komponen mengelola utas untuk Anda, menghindari sebagian besar hasil warna-warni dari pemotretan kaki, dan itu tidak memerlukan versi bahasa C # yang berdarah.
Bisa saja, tapi ini peretasan.
Lihat Apakah DoEvents Evil? .
Langsung dari halaman MSDN yang thedev dirujuk:
Memanggil metode ini menyebabkan utas saat ini ditangguhkan sementara semua pesan jendela tunggu diproses. Jika pesan menyebabkan suatu peristiwa dipicu, maka area lain dari kode aplikasi Anda dapat dieksekusi. Ini dapat menyebabkan aplikasi Anda menunjukkan perilaku tak terduga yang sulit di-debug. Jika Anda melakukan operasi atau perhitungan yang membutuhkan waktu lama, sering kali lebih baik melakukan operasi tersebut pada utas baru. Untuk informasi lebih lanjut tentang pemrograman asinkron, lihat Ikhtisar Pemrograman Asinkron.
Jadi Microsoft memperingatkan agar tidak menggunakannya.
Juga, saya menganggapnya sebagai peretasan karena perilakunya tidak dapat diprediksi dan rawan efek samping (ini berasal dari pengalaman mencoba menggunakan DoEvents alih-alih memutar utas baru atau menggunakan pekerja latar belakang).
Tidak ada machismo di sini - jika itu berfungsi sebagai solusi yang kuat saya akan mengatasinya. Namun, mencoba menggunakan DoEvents di .NET telah menyebabkan saya tidak merasa sakit.
BackgroundWorker
membantu menyederhanakan cara yang "benar".
Ya, ada metode DoEvents statis di kelas Aplikasi di namespace System.Windows.Forms. System.Windows.Forms.Application.DoEvents () dapat digunakan untuk memproses pesan yang menunggu dalam antrian pada utas UI saat melakukan tugas yang sudah berjalan lama pada utas UI. Ini memiliki manfaat membuat UI tampak lebih responsif dan tidak "terkunci" saat tugas panjang sedang berjalan. Namun, ini hampir selalu BUKAN cara terbaik untuk melakukan sesuatu. Menurut Microsoft yang memanggil DoEvents "... menyebabkan utas saat ini ditangguhkan sementara semua pesan jendela tunggu diproses." Jika suatu peristiwa dipicu ada potensi bug yang tak terduga dan intermiten yang sulit dilacak. Jika Anda memiliki tugas yang luas, jauh lebih baik melakukannya di utas terpisah. Menjalankan tugas panjang dalam utas terpisah memungkinkannya diproses tanpa mengganggu UI yang terus berjalan dengan lancar. Lihatdi sini untuk lebih jelasnya.
Berikut adalah contoh cara menggunakan DoEvents; perhatikan bahwa Microsoft juga memberikan peringatan untuk tidak menggunakannya.
Dari pengalaman saya, saya akan menyarankan sangat berhati-hati dengan menggunakan DoEvents di .NET. Saya mengalami beberapa hasil yang sangat aneh ketika menggunakan DoEvents di TabControl yang berisi DataGridViews. Di sisi lain, jika semua yang Anda hadapi adalah formulir kecil dengan bilah kemajuan maka mungkin tidak apa-apa.
Intinya adalah: jika Anda akan menggunakan DoEvents, maka Anda perlu mengujinya secara menyeluruh sebelum menggunakan aplikasi Anda.
Iya.
Namun, jika Anda perlu menggunakan Application.DoEvents
, ini sebagian besar merupakan indikasi desain aplikasi yang buruk. Mungkin Anda ingin melakukan pekerjaan di utas terpisah sebagai gantinya?
Saya melihat komentar jheriko di atas dan pada awalnya setuju bahwa saya tidak dapat menemukan cara untuk menghindari penggunaan DoEvents jika Anda akhirnya memutar utas UI utama Anda menunggu sepotong kode asinkron yang berjalan lama pada utas lain untuk diselesaikan. Tetapi dari jawaban Matthias, Refresh sederhana dari sebuah panel kecil di UI saya dapat menggantikan DoEvents (dan menghindari efek samping yang buruk).
Lebih detail tentang kasus saya ...
Saya sedang melakukan hal berikut (seperti yang disarankan di sini ) untuk memastikan bahwa layar splash type bar kemajuan ( Cara menampilkan overlay "memuat" ... ) diperbarui selama perintah SQL yang berjalan lama:
IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted) //UI thread needs to Wait for Async SQL command to return
{
System.Threading.Thread.Sleep(10);
Application.DoEvents(); //to make the UI responsive
}
Yang buruk: Bagi saya menelepon DoEvents berarti bahwa klik mouse kadang-kadang menembaki formulir di belakang layar splash saya, bahkan jika saya membuatnya TopMost.
Jawabannya: ganti baris DoEvents dengan panggilan Refresh sederhana ke panel kecil di tengah layar splash saya FormSplash.Panel1.Refresh()
,. UI memperbarui dengan baik dan keanehan DoEvents yang telah diperingatkan orang lain telah hilang.
Saya telah melihat banyak aplikasi komersial, menggunakan "DoEvents-Hack". Terutama ketika rendering mulai berperan, saya sering melihat ini:
while(running)
{
Render();
Application.DoEvents();
}
Mereka semua tahu tentang kejahatan metode itu. Namun, mereka menggunakan peretasan, karena mereka tidak tahu solusi lain. Berikut adalah beberapa pendekatan yang diambil dari posting blog oleh Tom Miller :
- Setel formulir Anda agar semua gambar muncul di WmPaint, dan lakukan rendering Anda di sana. Sebelum akhir metode OnPaint, pastikan Anda melakukan ini.Invalidate (); Ini akan menyebabkan metode OnPaint dipecat lagi dengan segera.
- P / Panggil API Win32 dan panggil PeekMessage / TranslateMessage / DispatchMessage. (Doevents sebenarnya melakukan sesuatu yang serupa, tetapi Anda dapat melakukan ini tanpa alokasi tambahan).
- Tulis kelas formulir Anda sendiri yang merupakan pembungkus kecil di sekitar CreateWindowEx, dan beri diri Anda kontrol penuh atas loop pesan. -Tentukan bahwa metode DoEvents berfungsi dengan baik untuk Anda dan tetap menggunakannya.
Lihatlah Dokumentasi MSDN untuk Application.DoEvents
metode ini.
DoEvents memungkinkan pengguna untuk mengklik atau mengetik dan memicu acara lainnya, dan utas latar belakang adalah pendekatan yang lebih baik.
Namun, masih ada kasus di mana Anda mungkin mengalami masalah yang membutuhkan pembilasan pesan acara. Saya mengalami masalah di mana kontrol RichTextBox mengabaikan metode ScrollToCaret () ketika kontrol memiliki pesan dalam antrian untuk diproses.
Kode berikut memblokir semua input pengguna saat menjalankan DoEvents:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Integrative.Desktop.Common
{
static class NativeMethods
{
#region Block input
[DllImport("user32.dll", EntryPoint = "BlockInput")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
public static void HoldUser()
{
BlockInput(true);
}
public static void ReleaseUser()
{
BlockInput(false);
}
public static void DoEventsBlockingInput()
{
HoldUser();
Application.DoEvents();
ReleaseUser();
}
#endregion
}
}
Application.DoEvents dapat membuat masalah, jika sesuatu selain pemrosesan grafis dimasukkan ke dalam antrian pesan.
Ini dapat berguna untuk memperbarui bilah kemajuan dan memberi tahu pengguna tentang kemajuan dalam sesuatu seperti konstruksi dan pemuatan MainForm, jika itu membutuhkan waktu.
Dalam aplikasi terbaru yang saya buat, saya menggunakan DoEvents untuk memperbarui beberapa label pada Layar Pemuatan setiap kali blok kode dieksekusi dalam konstruktor MainForm saya. Utas UI adalah, dalam hal ini, sibuk dengan mengirim email pada server SMTP yang tidak mendukung panggilan SendAsync (). Saya mungkin bisa membuat utas yang berbeda dengan metode Begin () dan End () dan memanggil Send () dari mereka, tetapi metode itu rawan kesalahan dan saya lebih suka Formulir Utama dari aplikasi saya tidak membuang pengecualian selama konstruksi.
DoEvents
adalah bagian dari Formulir Windows, bukan bahasa C #. Dengan demikian, dapat digunakan dari bahasa .NET. Namun, itu tidak boleh digunakan dari bahasa .NET.