Can Application.DoEvents()
C # kullanılacak?
Bu işlev, GUI'nin uygulamanın geri kalanını VB6'nın yaptığı DoEvents
gibi yakalamasına izin vermenin bir yolu mu?
Can Application.DoEvents()
C # kullanılacak?
Bu işlev, GUI'nin uygulamanın geri kalanını VB6'nın yaptığı DoEvents
gibi yakalamasına izin vermenin bir yolu mu?
Yanıtlar:
Hmya, DoEvents () kalıcı mistik. Buna karşı muazzam bir tepki vardı, ama hiç kimse bunun neden "kötü" olduğunu açıklamıyor. "Bir yapıyı değiştirmeyin" ile aynı bilgelik. Eee, çalışma zamanı ve dil neden bu kadar kötü ise bir yapıyı değiştirmeyi destekliyor? Aynı sebep: eğer doğru yapmazsan kendini ayağa vuruyorsun. Kolayca. Ve doğru yapmak, tam olarak ne yaptığını bilmeyi gerektirir , bu da DoEvents () durumunda kesinlikle grok yapmak kolay değildir.
Hemen yarasa: neredeyse tüm Windows Forms programları aslında bir DoEvents () çağrısı içerir. Zekice gizlenmiş, ancak farklı bir adla: ShowDialog (). Bir iletişim kutusunun uygulamadaki diğer pencereleri dondurmadan kalıcı olmasını sağlayan DoEvents () işlevidir.
Çoğu programcı kendi modal döngülerini yazdıklarında kullanıcı arayüzlerinin donmasını durdurmak için DoEvents kullanmak ister. Bunu kesinlikle yapar; Windows iletileri gönderir ve tüm boya isteklerini alır. Ancak sorun seçici olmamasıdır. Sadece boya mesajları göndermekle kalmaz, diğer her şeyi de sunar.
Ve soruna neden olan bir dizi bildirim var. Monitörün yaklaşık 3 feet önünde geliyorlar. Örneğin kullanıcı, DoEvents () öğesini çağıran döngü çalışırken ana pencereyi kapatabilir. Bu işe yarar, kullanıcı arayüzü gitti. Ancak kodunuz durmadı, hala döngüyü yürütüyor. Bu kötü. Çok çok kötü.
Dahası da var: Kullanıcı, aynı döngünün başlamasına neden olan aynı menü öğesini veya düğmeyi tıklatabilir. Artık DoEvents () işlemini yürüten iki iç içe döngünüz var, önceki döngü askıya alındı ve yeni döngü sıfırdan başlıyor. Bu işe yarayabilir, ama çocuk oranlar zayıf. Özellikle iç içe döngü sona erdiğinde ve askıya alınmış olanı devam ettiğinde, zaten tamamlanmış bir işi bitirmeye çalışır. Bu bir istisna dışında bomba olmazsa, elbette veriler cehenneme karıştırılır.
ShowDialog () sayfasına geri dön. DoEvents () yürütür, ancak başka bir şey yaptığını unutmayın. Bu uygulamadaki tüm pencereleri kapatır iletişim dışındaki,. Artık 3 metrelik problem çözüldü, kullanıcı mantığı bozmak için hiçbir şey yapamaz. Hem pencereyi kapat hem de işi yeniden başlat hata modları çözülür. Veya başka bir deyişle, kullanıcının programınızın kodunu farklı bir sırayla çalıştırmasını sağlamanın bir yolu yoktur. Kodunuzu test ettiğinizde olduğu gibi tahmin edilebilir bir şekilde yürütülür. Diyalogları son derece can sıkıcı hale getirir; kim aktif bir diyalog kurmaktan ve başka bir pencereden bir şey kopyalayıp yapıştırmaktan nefret etmez? Ama fiyat bu.
DoEvents kodunuzda güvenle kullanmak için gereken budur. Tüm formlarınızın Enabled özelliğini false olarak ayarlamak sorunlardan kaçınmanın hızlı ve etkili bir yoludur. Tabii ki, hiçbir programcı bunu yapmayı gerçekten sevmez. Ve istemiyor. Bu yüzden DoEvents () kullanmamalısınız. Konu kullanmalısınız. Ayaklarınızı renkli ve anlaşılmaz yollarla vurmanın tam bir cephaneliğini vermelerine rağmen. Ama sadece kendi ayağını vurmanın avantajı ile; (tipik olarak) kullanıcının kendi fotoğrafını çekmesine izin vermez.
C # ve VB.NET'in sonraki sürümleri, yeni bekleme ve zaman uyumsuz anahtar kelimelerle farklı bir tabanca sağlayacaktır. DoEvents ve iş parçacıklarının neden olduğu sorundan küçük bir miktar, ancak asenkron işlem yapılırken UI'nizi güncel tutmanızı gerektiren WinRT API tasarımından ilham alındı . Bir dosyadan okumak gibi.
BackgroundWorker
bileşen sizin için konuları yönetir, ayak çekiminin renkli sonuçlarının çoğunu önler ve kanama kenarı C # dil sürümleri gerektirmez.
Olabilir, ama bu bir hack.
Bakınız DoEvents Evil mi? .
Doğrudan MSDN sayfasında şu thedev başvurulan:
Bu yöntemin çağrılması, bekleyen tüm pencere iletileri işlenirken geçerli iş parçacığının askıya alınmasına neden olur. Bir mesaj bir olayın tetiklenmesine neden olursa, uygulama kodunuzun diğer alanları da yürütülebilir. Bu, uygulamanızın hata ayıklaması zor olan beklenmedik davranışlar göstermesine neden olabilir. Uzun zaman alan işlemler veya hesaplamalar yaparsanız, bu işlemleri yeni bir iş parçacığında gerçekleştirmek genellikle tercih edilir. Eşzamansız programlama hakkında daha fazla bilgi için bkz. Eşzamansız Programlamaya Genel Bakış.
Bu yüzden Microsoft, kullanımına karşı uyarıyor.
Ayrıca, davranışı öngörülemeyen ve yan etkiye eğilimli olduğu için bunu bir hack olarak görüyorum (bu, yeni bir iş parçacığını döndürmek veya arka plan çalışanı kullanmak yerine DoEvents kullanmaya çalışmanın deneyiminden kaynaklanmaktadır).
Burada hiçbir machismo yok - eğer sağlam bir çözüm olarak çalışırsa her şeyin üstünde olurdum. Ancak, .NET'te DoEvents kullanmaya çalışmak bana acıdan başka bir şey yapmadı.
BackgroundWorker
ve "doğru" yolun basitleştirilmesine yardımcı olduğunu belirtmek gerekir .
Evet, System.Windows.Forms ad alanındaki Application sınıfında statik bir DoEvents yöntemi vardır. UI iş parçacığında uzun süre çalışan bir görev gerçekleştirirken System.Windows.Forms.Application.DoEvents (), UI iş parçacığında sırada bekleyen iletileri işlemek için kullanılabilir. Bu, uzun bir görev çalışırken kullanıcı arayüzünün daha duyarlı görünmesini ve "kilitlenmemesini" sağlar. Ancak, bu neredeyse her zaman bir şeyler yapmanın en iyi yolu DEĞİLDİR. Microsoft arama DoEvents göre "... tüm bekleyen pencere mesajları işlenirken geçerli iş parçacığı askıya neden olur." Bir olay tetiklenirse, izlenmesi zor olan beklenmedik ve aralıklı hatalar olasılığı vardır. Kapsamlı bir göreviniz varsa, ayrı bir iş parçacığında yapmak çok daha iyidir. Uzun görevleri ayrı bir iş parçacığında çalıştırmak, sorunsuz bir şekilde çalışmaya devam eden kullanıcı arayüzüne müdahale etmeden işlenmelerini sağlar. Bakdaha fazla bilgi için buraya tıklayın .
İşte DoEvents nasıl kullanılacağı ile ilgili bir örnek; Microsoft'un da kullanımına karşı bir uyarı verdiğini unutmayın.
Deneyimlerimden, .NET'te DoEvents kullanarak büyük dikkat gösterilmesini tavsiye ederim. DataGridViews içeren bir TabControl DoEvents kullanırken bazı çok garip sonuçlar yaşadı. Öte yandan, uğraştığınız tek şey ilerleme çubuğuna sahip küçük bir formsa, sorun olmayabilir.
Sonuç olarak: DoEvents kullanacaksanız, uygulamanızı dağıtmadan önce iyice test etmeniz gerekir.
Evet.
Ancak, kullanmanız gerekiyorsa Application.DoEvents
, bu çoğunlukla kötü uygulama tasarımının bir göstergesidir. Belki de bunun yerine ayrı bir iş parçacığında bazı işler yapmak istersiniz?
Yukarıda jheriko'nun yorumunu gördüm ve başlangıçta, başka bir iş parçacığında uzun süre çalışan bir eşzamansız kod parçasını bekleyen ana UI iş parçacığınızı döndürdüğünüzde DoEvents kullanmaktan kaçınmanın bir yolunu bulamadığımı kabul ediyordum. Ancak Matthias'ın cevabından, arayüzümdeki küçük bir panelin yenilenmesi DoEvents'ın yerini alabilir (ve kötü bir yan etkiyi önleyebilir).
Davam hakkında daha fazla detay ...
Bir ilerleme çubuğu türü açılış ekranı ( "yükleme" kaplaması nasıl görüntülenir ... ) uzun süre çalışan bir SQL komutu sırasında güncellendiğinden emin olmak için ( burada önerildiği gibi ) aşağıdaki yapıyordum :
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
}
Kötü: Benim için DoEvents'ı çağırmak, fare tıklamaları bazen açılış ekranımın arkasındaki formlara ateş ediyordu, hatta TopMost yaptım.
İyi / yanıt: DoEvents satırını açılış ekranımın ortasındaki küçük bir panele basit bir Yenile çağrısı ile değiştirin FormSplash.Panel1.Refresh()
. Kullanıcı arayüzü güzel bir şekilde güncelleniyor ve başkalarının uyardığı DoEvents tuhaflığı ortadan kalktı.
"DoEvents-Hack" kullanarak birçok ticari uygulama gördüm. Özellikle görüntü oluşturma söz konusu olduğunda bunu sıklıkla görüyorum:
while(running)
{
Render();
Application.DoEvents();
}
Hepsi bu yöntemin kötülüğünü biliyor. Ancak, hack kullanıyorlar, çünkü başka bir çözüm bilmiyorlar. İşte alınan bazı yaklaşımlardır blog yayınında tarafından Tom Miller :
- Formunuzu, tüm çizimin WmPaint'te gerçekleşmesini sağlayacak şekilde ayarlayın ve oluşturma işleminizi orada yapın. OnPaint yönteminin bitiminden önce bunu yaptığınızdan emin olun.Invalidate (); Bu, OnPaint yönteminin hemen yeniden başlatılmasına neden olur.
- P / Win32 API içine çağırmak ve PeekMessage / TranslateMessage / DispatchMessage arayın. (Doevents aslında benzer bir şey yapar, ancak bunu ekstra tahsisler olmadan yapabilirsiniz).
- CreateWindowEx çevresinde küçük bir paket olan kendi form sınıfınızı yazın ve ileti döngüsü üzerinde kendinize tam denetim sağlayın. - DoEvents yönteminin sizin için iyi çalıştığından emin olun ve buna uyun.
Application.DoEvents
Yöntem için MSDN Belgelerine bakın .
DoEvents, kullanıcının diğer olayları tıklatmasına veya yazmasına ve tetiklemesine izin verir ve arka plan iş parçacıkları daha iyi bir yaklaşımdır.
Ancak, olay iletilerini silmeyi gerektiren sorunlarla karşılaşabileceğiniz durumlar da vardır. Ne zaman kontrol işlemek için sıradaki iletileri vardı RichTextBox denetiminin ScrollToCaret () yöntemi yoksayıyordu bir sorunla karşılaştım.
Aşağıdaki kod, DoEvents çalıştırırken tüm kullanıcı girişlerini engeller:
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, mesaj kuyruğuna grafik işleme dışında bir şey konursa sorun oluşturabilir.
İlerleme çubuklarını güncellemek ve MainForm inşaat ve yükleme gibi bir şeydeki ilerlemeyi kullanıcıya bildirmek için yararlı olabilir, eğer bu biraz zaman alır.
Yaptığım son bir uygulamada, MainForm'umun yapıcısında bir kod bloğu her yürütüldüğünde bir Yükleme Ekranındaki bazı etiketleri güncellemek için DoEvents kullandım. UI iş parçacığı, bu durumda, SendAsync () çağrılarını desteklemeyen bir SMTP sunucusuna e-posta göndermekle meşgul oldu. Muhtemelen Begin () ve End () yöntemleri ile farklı bir iş parçacığı oluşturabilir ve onlardan bir Send () çağırdı olabilir, ancak bu yöntem hata eğilimli ve ben uygulama sırasında Ana Form inşaat sırasında istisna atma tercih etmem.
DoEvents
C # dilinin değil, Windows Forms'ın bir parçasıdır. Böyle olunca edebilirsiniz herhangi NET dilden kullanılabilir. Ancak, olmamalıdır herhangi NET dilden kullanılabilir.