Làm thế nào tôi có thể làm cho con trỏ chuyển sang con trỏ chờ?


263

Tôi có một ứng dụng C # có người dùng đăng nhập vào nó và vì thuật toán băm rất tốn kém, nên phải mất một chút thời gian để làm. Làm cách nào tôi có thể hiển thị Wait / Busy Coder (thường là đồng hồ cát) cho người dùng để cho họ biết chương trình đang làm gì đó?

Dự án nằm trong C #.

Câu trả lời:


451

Bạn có thể sử dụng 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;

Tuy nhiên, nếu thao tác băm thực sự dài (MSDN định nghĩa điều này là hơn 2-7 giây), có lẽ bạn nên sử dụng chỉ báo phản hồi trực quan khác với con trỏ để thông báo cho người dùng về tiến trình. Để có một bộ hướng dẫn sâu hơn, xem bài viết này .

Chỉnh sửa:
Như @Am đã chỉ ra, bạn có thể cần gọi Application.DoEvents();sau Cursor.Current = Cursors.WaitCursor;để đảm bảo rằng đồng hồ cát thực sự được hiển thị.


23
điều này sẽ không cần thiết thay đổi con trỏ - nếu vòng lặp thông báo sẽ không được gọi trong mã sử dụng nhiều thời gian. để kích hoạt nó, bạn cần thêm Application.DoEvents (); sau khi đặt con trỏ đầu tiên.
Amirshk

16
Bạn cũng có thể muốn thử .. cuối cùng cũng được đặt sau khi cài đặt Hiện tại (đảm bảo rằng Hiện tại được đặt lại về Mặc định).
TrueWill

7
FYI, tôi không thể làm cho những thứ trên hoạt động được, nhưng bằng cách thay đổi nó thành this.coder = cursors.waitcthon; nó đã làm việc.
Hans Rudel

4
Đồng hồ cát không được hiển thị nếu tôi sử dụng Application.DoEvents () sau Cthon.C hiện = Cursors.WaitCoder Tuy nhiên, nó đã hoạt động mà không có Application.DoEvents (). Không chắc chắn Tại sao
Vbp

14
Nó là tốt hơn để sử dụng Application.UseWaitCursor = trueApplication.UseWaitCursor = false
Gianpiero

169

Thực ra,

Cursor.Current = Cursors.WaitCursor;

tạm thời đặt con trỏ Chờ, nhưng không đảm bảo rằng con trỏ Chờ hiển thị cho đến khi kết thúc thao tác của bạn. Các chương trình hoặc điều khiển khác trong chương trình của bạn có thể dễ dàng đặt lại con trỏ trở lại mũi tên mặc định như trên thực tế xảy ra khi bạn di chuyển chuột trong khi hoạt động vẫn đang chạy.

Một cách tốt hơn để hiển thị con trỏ Chờ là đặt thuộc tính UseWaitC tiền ở dạng thành đúng:

form.UseWaitCursor = true;

Điều này sẽ hiển thị con trỏ chờ cho tất cả các điều khiển trên biểu mẫu cho đến khi bạn đặt thuộc tính này thành false. Nếu bạn muốn con trỏ chờ được hiển thị ở cấp Ứng dụng, bạn nên sử dụng:

Application.UseWaitCursor = true;

Tốt để biết. Tôi đã cố gắng làm điều tương tự trong WPF, và kết thúc với Coder = Cursors.WaitCoder = Cursors.Arrow . Nhưng tôi không thể tìm thấy Con trỏ trong Ứng dụng
itho

2
Không thể tìm thấy UseWaitC tiền trong Ứng dụng!
Chandra Eskay

Tôi thấy rằng, khi cài đặt form.UseWaitCthon = false khi kết thúc thao tác, nó không thực sự đặt lại con trỏ cho đến khi bạn di chuyển hoặc nhấp chuột. OTOH, form.Coder không có vấn đề này. Tôi không thể có được Coder. Hiện tại để làm việc.
Stewart

39

Dựa trên cách tiếp cận trước đây, cách tiếp cận ưa thích của tôi (vì đây là hành động được thực hiện thường xuyên) là bọc mã con trỏ chờ trong lớp trình trợ giúp IDis Dùng để nó có thể được sử dụng bằng cách sử dụng () (một dòng mã), lấy tham số tùy chọn, chạy mã bên trong, sau đó dọn sạch (khôi phục con trỏ) sau đó.

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

Sử dụng:

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


Không thấy điều đó, nhưng có cách tiếp cận tương tự. Anh ta sao lưu con trỏ hiện tại sau đó khôi phục nó, điều này có thể hữu ích nếu bạn đang thay đổi con trỏ nặng.
mhapps

29

Sử dụng UseWaitC tiền ở cấp Biểu mẫu hoặc Cửa sổ sẽ dễ dàng hơn . Một trường hợp sử dụng điển hình có thể trông như dưới đây:

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

Để có trải nghiệm UI tốt hơn, bạn nên sử dụng Không đồng bộ từ một luồng khác.


2
Đây phải là câu trả lời CHẤP NHẬN. Đây là người duy nhất sử dụng thử - cuối cùng.
John Henckel

1
có upvote của tôi, tôi đã bỏ lỡ một thử - cuối cùng trong quá trình thực hiện của tôi
Jack

19

Cách tiếp cận của tôi sẽ là thực hiện tất cả các tính toán trong một nhân viên nền.

Sau đó thay đổi con trỏ như thế này:

this.Cursor = Cursors.Wait;

Và trong sự kiện kết thúc của chủ đề, khôi phục con trỏ:

this.Cursor = Cursors.Default;

Lưu ý, điều này cũng có thể được thực hiện cho các điều khiển cụ thể, vì vậy con trỏ sẽ chỉ là đồng hồ cát khi chuột ở trên chúng.


@Malfist: cách tiếp cận tốt :), sau đó tất cả những gì bạn cần làm là đặt khôi phục trong sự kiện kết thúc và bạn đã hoàn tất.
Amirshk

4

OK vì vậy tôi đã tạo ra một phương thức async tĩnh. Điều đó đã vô hiệu hóa điều khiển khởi chạy hành động và thay đổi con trỏ ứng dụng. Nó chạy hành động như một nhiệm vụ và chờ đợi để kết thúc. Kiểm soát trả về cho người gọi trong khi nó chờ. Vì vậy, ứng dụng vẫn phản hồi, ngay cả khi biểu tượng bận rộn quay.

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

Đây là mẫu mã mẫu chính

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

Tôi đã phải sử dụng một trình ghi nhật ký riêng cho hành động giả (tôi đang sử dụng Nlog) và trình ghi nhật ký chính của tôi đang ghi vào UI (một hộp văn bản có định dạng). Tôi không thể hiển thị con trỏ bận khi chỉ trên một vùng chứa cụ thể trên biểu mẫu (nhưng tôi đã không cố gắng hết sức.) Tất cả các điều khiển đều có thuộc tính UseWaitCoder, nhưng dường như nó không có bất kỳ ảnh hưởng nào đến các điều khiển Tôi đã thử (có thể vì họ không ở trên đầu?)

Đây là nhật ký chính, cho thấy những điều xảy ra theo thứ tự chúng tôi mong đợi:

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

Với lớp học bên dưới, bạn có thể đưa ra gợi ý về Donut "ngoại lệ an toàn".

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

lớp CoderHandler

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, quan điểm của người khác rất rõ ràng, nhưng tôi muốn thêm vào, như sau:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

2

Đối với các ứng dụng Windows Forms, việc vô hiệu hóa tùy chọn UI-Control có thể rất hữu ích. Vì vậy, đề nghị của tôi trông như thế này:

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

Sử dụng:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.