İmlecin bekleme imlecine nasıl dönmesini sağlayabilirim?


263

Kullanıcıların oturum açma olan bir C # uygulaması var ve karma algoritması pahalı olduğundan, biraz zaman alır. Programın bir şey yaptığını bildirmek için kullanıcıya Bekle / Meşgul İmlecini (genellikle kum saati) nasıl görüntüleyebilirim?

Proje C # 'dadır.

Yanıtlar:


451

Kullanabilirsiniz Cursor.Current.

// Set cursor as hourglass
Cursor.Current = Cursors.WaitCursor;

// Execute your time-intensive hashing code here...

// Set cursor as default arrow
Cursor.Current = Cursors.Default;

Ancak, karma işlemi gerçekten uzunsa (MSDN bunu 2-7 saniyeden fazla olarak tanımlar), kullanıcıyı ilerlemeyi bildirmek için imleç dışında görsel bir geri bildirim göstergesi kullanmanız gerekir. Daha ayrıntılı yönergeler için bu makaleye bakın .

Düzenleme:
@Am belirttiği gibi , kum saati gerçekten görüntülendiğinden emin olmak için Application.DoEvents();sonra çağırmanız gerekebilir Cursor.Current = Cursors.WaitCursor;.


23
bu, imleci değiştirmeye gerek kalmaz - zaman yoğun kod sırasında mesaj döngüsü çağrılmazsa. etkinleştirmek için Application.DoEvents () eklemeniz gerekir ; İlk imleç kümesinden sonra.
Amirshk

16
Akım ayarlandıktan sonra da bir try..finally engellemesi istersiniz (Akımın Varsayılana sıfırlandığından emin olun).
TrueWill

7
Bilginize, yukarıdaki işe yaramadı, ama bunu this.cursor = cursors.waitcursor olarak değiştirerek; işe yaradı.
Hans Rudel

4
Cursor.Current = Cursors.WaitCursor sonra Application.DoEvents () kullandıysam kum saati görüntülenmedi Ancak, Application.DoEvents () olmadan çalıştı. Emin değilim Neden
Vbp

14
Kullanmak daha iyidir Application.UseWaitCursor = trueveApplication.UseWaitCursor = false
Gianpiero

169

Aslında,

Cursor.Current = Cursors.WaitCursor;

Bekle imlecini geçici olarak ayarlar, ancak Bekle imlecinin işleminizin sonuna kadar gösterilmesini sağlamaz. Programınızdaki diğer programlar veya kontroller, işlem devam ederken fareyi hareket ettirdiğinizde olduğu gibi imleci varsayılan oka kolayca sıfırlayabilir.

Bekle imlecini göstermenin çok daha iyi bir yolu, bir formdaki UseWaitCursor özelliğini true olarak ayarlamaktır:

form.UseWaitCursor = true;

Bu, siz bu özelliği false değerine ayarlayana kadar formdaki tüm denetimler için bekleme imlecini görüntüler. Bekle imlecinin Uygulama düzeyinde gösterilmesini istiyorsanız şunları kullanmalısınız:

Application.UseWaitCursor = true;

Bunu bildiğim iyi oldu. WPF de aynısını yapmaya çalışıyordu ve Cursor = Cursors.Wait ve Cursor = Cursors.Arrow ile sona erdi . Ama imleci App
itsho

2
Uygulama altında UseWaitCursor bulunamadı!
Chandra Eskay

Formun kullanımında işlemin sonunda = UseWaitCursor = false, fareyi hareket ettirene veya tıklayana kadar aslında imleci sıfırlamadığını buldum. OTOH, form.Cursor bu sorunu yaşamıyor. Hiç imleç alamadım.
Stewart

39

Bir öncekine dayanarak, tercih ettiğim yaklaşım (bu sık gerçekleştirilen bir eylem olduğu için) bekleme imleci kodunu IDisposable yardımcı sınıfına sarmaktır, böylece () (bir kod satırı) ile kullanılabilir, isteğe bağlı parametreler alır, çalıştırılır içindeki kodu girin ve daha sonra temizleyin (imleci geri yükle).

public class CursorWait : IDisposable
{
    public CursorWait(bool appStarting = false, bool applicationCursor = false)
    {
        // Wait
        Cursor.Current = appStarting ? Cursors.AppStarting : Cursors.WaitCursor;
        if (applicationCursor) Application.UseWaitCursor = true;
    }

    public void Dispose()
    {
        // Reset
        Cursor.Current = Cursors.Default;
        Application.UseWaitCursor = false;
    }
}

Kullanımı:

using (new CursorWait())
{
    // Perform some code that shows cursor
}


Bunu görmemiştim ama evet benzer bir yaklaşım. Geçerli imleci yedekler ve daha sonra geri yükler, bu da ağır imleç değişikliği yapıyorsanız faydalı olabilir.
mhapps

29

UseWaitCursor öğesini Form veya Pencere düzeyinde kullanmak daha kolaydır . Tipik bir kullanım durumu aşağıdaki gibi görünebilir:

    private void button1_Click(object sender, EventArgs e)
    {

        try
        {
            this.Enabled = false;//optional, better target a panel or specific controls
            this.UseWaitCursor = true;//from the Form/Window instance
            Application.DoEvents();//messages pumped to update controls
            //execute a lengthy blocking operation here, 
            //bla bla ....
        }
        finally
        {
            this.Enabled = true;//optional
            this.UseWaitCursor = false;
        }
    }

Daha iyi bir kullanıcı arayüzü deneyimi için Asynchrony'yi farklı bir iş parçacığından kullanmalısınız.


2
Bu KABUL EDİLMİŞ yanıt olmalıdır. Try-nihayet kullanan tek kişi.
John Henckel

1
benim oyumu var, benim uygulamada bir deneme-nihayet eksik
Jack

19

Benim yaklaşımım, tüm hesaplamaları bir arka plan çalışanında yapmak olacaktır.

Sonra imleci şu şekilde değiştirin:

this.Cursor = Cursors.Wait;

Ve iş parçacığının bitiş etkinliğinde imleci geri yükleyin:

this.Cursor = Cursors.Default;

Bu, belirli kontroller için de yapılabilir, bu nedenle imleç yalnızca fare üstlerinde olduğunda kum saati olacaktır.


@Malfist: iyi bir yaklaşım :), o zaman tek yapmanız gereken geri yüklemeyi son etkinliğe yerleştirmektir ve işiniz bitti.
Amirshk

4

Tamam bu yüzden statik bir zaman uyumsuzluk yöntemi oluşturdum. Bu, eylemi başlatan ve uygulama imlecini değiştiren denetimi devre dışı bırakır. Eylemi bir görev olarak yürütür ve bitmesini bekler. Kontrol beklerken arayana geri döner. Böylece meşgul simgesi dönse bile uygulama yanıt verir.

async public static void LengthyOperation(Control control, Action action)
{
    try
    {
        control.Enabled = false;
        Application.UseWaitCursor = true;
        Task doWork = new Task(() => action(), TaskCreationOptions.LongRunning);
        Log.Info("Task Start");
        doWork.Start();
        Log.Info("Before Await");
        await doWork;
        Log.Info("After await");
    }
    finally
    {
        Log.Info("Finally");
        Application.UseWaitCursor = false;
        control.Enabled = true;
    }

İşte ana formu oluşturan kod

    private void btnSleep_Click(object sender, EventArgs e)
    {
        var control = sender as Control;
        if (control != null)
        {
            Log.Info("Launching lengthy operation...");
            CursorWait.LengthyOperation(control, () => DummyAction());
            Log.Info("...Lengthy operation launched.");
        }

    }

    private void DummyAction()
    {
        try
        {
            var _log = NLog.LogManager.GetLogger("TmpLogger");
            _log.Info("Action - Sleep");
            TimeSpan sleep = new TimeSpan(0, 0, 16);
            Thread.Sleep(sleep);
            _log.Info("Action - Wakeup");
        }
        finally
        {
        }
    }

Ben kukla eylem (Nlog kullanıyorum) için ayrı bir logger kullanmak zorunda kaldı ve benim ana logger UI (zengin bir metin kutusu) yazıyor. Meşgul imleci yalnızca formdaki belirli bir kapsayıcı üzerinde zaman elde edemedim (ama çok denemedim.) Tüm denetimlerin bir UseWaitCursor özelliği var, ancak denetimleri üzerinde herhangi bir etkisi yok gibi görünüyor Denedim (belki de üstte olmadıkları için?)

Beklediğiniz sırayla olan şeyleri gösteren ana günlük:

16:51:33.1064 Launching lengthy operation...
16:51:33.1215 Task Start
16:51:33.1215 Before Await
16:51:33.1215 ...Lengthy operation launched.
16:51:49.1276 After await
16:51:49.1537 Finally

2

Aşağıdaki sınıf ile Donut "istisna güvenli" önerisini yapabilirsiniz.

using (new CursorHandler())
{
    // Execute your time-intensive hashing code here...
}

CursorHandler sınıfı

public class CursorHandler
    : IDisposable
{
    public CursorHandler(Cursor cursor = null)
    {
        _saved = Cursor.Current;
        Cursor.Current = cursor ?? Cursors.WaitCursor;
    }

    public void Dispose()
    {
        if (_saved != null)
        {
            Cursor.Current = _saved;
            _saved = null;
        }
    }

    private Cursor _saved;
}

2

Okey, Diğer insanların görüşü çok açık, ama aşağıdaki gibi bazı ekler yapmak istiyorum:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

2

Windows Forms uygulamaları için, bir UI-Control'ün isteğe bağlı olarak devre dışı bırakılması çok yararlı olabilir. Benim önerim şöyle:

public class AppWaitCursor : IDisposable
{
    private readonly Control _eventControl;

    public AppWaitCursor(object eventSender = null)
    {
         _eventControl = eventSender as Control;
        if (_eventControl != null)
            _eventControl.Enabled = false;

        Application.UseWaitCursor = true;
        Application.DoEvents();
    }

    public void Dispose()
    {
        if (_eventControl != null)
            _eventControl.Enabled = true;

        Cursor.Current = Cursors.Default;
        Application.UseWaitCursor = false;
    }
}

Kullanımı:

private void UiControl_Click(object sender, EventArgs e)
{
    using (new AppWaitCursor(sender))
    {
        LongRunningCall();
    }
}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.