Bagaimana saya bisa mengarahkan kursor ke kursor tunggu?

263

Saya memiliki aplikasi C # yang meminta pengguna untuk login, dan karena algoritma hashing mahal, perlu beberapa saat untuk melakukannya. Bagaimana saya bisa menampilkan Kursor Tunggu / Sibuk (biasanya jam pasir) kepada pengguna untuk memberi tahu mereka bahwa program sedang melakukan sesuatu?

Proyek ini dalam C #.

Malfist
sumber

Jawaban:

451

Anda bisa menggunakannya 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;

Namun, jika operasi hashing benar - benar panjang (MSDN mendefinisikan ini lebih dari 2-7 detik), Anda mungkin harus menggunakan indikator umpan balik visual selain kursor untuk memberi tahu pengguna tentang kemajuan. Untuk seperangkat pedoman yang lebih mendalam, lihat artikel ini .

Sunting:
Seperti yang ditunjukkan @Am, Anda mungkin perlu menelepon Application.DoEvents();setelah Cursor.Current = Cursors.WaitCursor;untuk memastikan bahwa jam pasir benar-benar ditampilkan.

Donat
sumber
23
ini tidak perlu mengubah kursor - jika loop pesan tidak akan dipanggil selama kode intensif waktu. untuk mengaktifkannya, Anda perlu menambahkan Application.DoEvents (); setelah kursor pertama ditetapkan.
Amirshk
16
Anda mungkin ingin mencoba .. akhirnya memblokir setelah mengatur Arus juga (memastikan bahwa Arus akan diatur ulang ke Default).
TrueWill
7
FYI, saya tidak bisa mendapatkan yang di atas berfungsi, tetapi dengan mengubahnya ke this.cursor = cursors.waitcursor; itu berhasil.
Hans Rudel
4
Jam pasir tidak ditampilkan jika saya menggunakan Application.DoEvents () setelah Cursor.Current = Cursors.WaitCursor Namun, itu berhasil tanpa Application.DoEvents (). Tidak yakin Mengapa
Vbp
14
Lebih baik menggunakan Application.UseWaitCursor = truedanApplication.UseWaitCursor = false
Gianpiero
169

Sebenarnya,

Cursor.Current = Cursors.WaitCursor;

tentukan sementara kursor Tunggu, tetapi tidak memastikan bahwa kursor Tunggu muncul hingga akhir operasi Anda. Program atau kontrol lain dalam program Anda dapat dengan mudah mengatur ulang kursor kembali ke panah default seperti yang sebenarnya terjadi ketika Anda menggerakkan mouse saat operasi masih berjalan.

Cara yang jauh lebih baik untuk menampilkan kursor Tunggu adalah dengan menyetel properti UseWaitCursor dalam bentuk menjadi true:

form.UseWaitCursor = true;

Ini akan menampilkan kursor tunggu untuk semua kontrol pada formulir sampai Anda menyetel properti ini menjadi false. Jika Anda ingin menunggu kursor ditampilkan pada level Aplikasi, Anda harus menggunakan:

Application.UseWaitCursor = true;
draganstankovic
sumber
Senang mendengarnya. Saya mencoba melakukan hal yang sama di WPF, dan berakhir dengan Cursor = Cursors.Wait dan Cursor = Cursors.Arrow . Tapi saya tidak dapat menemukan kursor di bawah App
itsho
2
Tidak dapat menemukan UseWaitCursor di bawah Aplikasi!
Chandra Eskay
Saya menemukan bahwa, ketika mengatur form.UseWaitCursor = false di akhir operasi, itu tidak benar-benar mereset kursor sampai Anda memindahkan atau mengklik mouse. OTOH, form.Cursor tidak memiliki masalah ini. Saya tidak bisa mendapatkan kursor. Saat ini bekerja sama sekali.
Stewart
39

Membangun sebelumnya, pendekatan yang saya pilih (karena ini adalah tindakan yang sering dilakukan) adalah untuk membungkus kode kursor tunggu dalam kelas pembantu IDisposable sehingga dapat digunakan dengan menggunakan () (satu baris kode), ambil parameter opsional, jalankan kode di dalamnya, lalu bersihkan (restore cursor) sesudahnya.

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;
    }
}

Pemakaian:

using (new CursorWait())
{
    // Perform some code that shows cursor
}
mhapps
sumber
Belum melihat itu, tapi ya pendekatan serupa. Dia mencadangkan kursor saat ini kemudian mengembalikannya, yang mungkin berguna jika Anda melakukan perubahan kursor berat.
mhapps
29

Lebih mudah untuk menggunakan UseWaitCursor di tingkat Form atau Window. Kasus penggunaan umum dapat terlihat seperti di bawah ini:

    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;
        }
    }

Untuk pengalaman UI yang lebih baik, Anda harus menggunakan Asynchrony dari utas yang berbeda.

dmihailescu
sumber
2
Ini harus menjadi jawaban yang DITERIMA. Ini adalah satu-satunya yang menggunakan try-akhirnya.
John Henckel
1
mendapatkan dukungan saya, saya melewatkan percobaan-akhirnya dalam implementasi saya
Jack
19

Pendekatan saya adalah membuat semua perhitungan di latar belakang pekerja.

Kemudian ubah kursor seperti ini:

this.Cursor = Cursors.Wait;

Dan di acara selesai utas mengembalikan kursor:

this.Cursor = Cursors.Default;

Catatan, ini juga dapat dilakukan untuk kontrol tertentu, sehingga kursor akan menjadi jam pasir hanya ketika mouse berada di atasnya.

Amirshk
sumber
@Malfist: pendekatan yang baik :), maka yang perlu Anda lakukan adalah menempatkan pengembalian di akhir acara, dan selesai.
Amirshk
4

OK jadi saya membuat metode async statis. Itu menonaktifkan kontrol yang meluncurkan aksi dan mengubah kursor aplikasi. Itu menjalankan tindakan sebagai tugas dan menunggu untuk selesai. Kontrol kembali ke pemanggil saat menunggu. Jadi aplikasi tetap responsif, bahkan saat ikon sibuk berputar.

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;
    }

Berikut kode dari form utama

    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
        {
        }
    }

Saya harus menggunakan logger terpisah untuk aksi dummy (saya menggunakan Nlog) dan logger utama saya menulis ke UI (kotak teks kaya). Saya tidak bisa mendapatkan tampilan kursor yang sibuk hanya ketika melewati wadah tertentu pada formulir (tapi saya tidak berusaha sangat keras). Semua kontrol memiliki properti UseWaitCursor, tetapi tampaknya tidak berpengaruh pada kontrol Saya mencoba (mungkin karena mereka tidak di atas?)

Inilah log utama, yang menunjukkan hal-hal yang terjadi dalam urutan yang kami harapkan:

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
Darrel Lee
sumber
2

Dengan kelas di bawah ini Anda dapat membuat saran dari Donut "pengecualian aman".

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

CursorHandler kelas

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;
}
Georg
sumber
2

Okey, pandangan orang lain sangat jelas, tetapi saya ingin menambahkan, sebagai berikut:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;
Wenha
sumber
2

Untuk aplikasi Windows Forms, penonaktifan opsional UI-Control bisa sangat berguna. Jadi saran saya terlihat seperti ini:

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;
    }
}

Pemakaian:

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

Gunakan ini dengan WPF:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;
Abdulrazzaq Alzayed
sumber