ฉันจะทำให้เคอร์เซอร์หันไปเป็นเคอร์เซอร์รอได้อย่างไร?


263

ฉันมีแอปพลิเคชัน C # ที่มีผู้ใช้ที่ลงชื่อเข้าใช้และเนื่องจากอัลกอริทึมการแปลงแป้นพิมพ์มีราคาแพงจึงต้องใช้เวลาสักครู่จึงจะทำ ฉันจะแสดงเคอร์เซอร์ Wait / Busy (ปกติคือนาฬิกาทราย) ให้ผู้ใช้ทราบได้อย่างไรว่าโปรแกรมกำลังทำอะไรอยู่

โครงการอยู่ใน C #

คำตอบ:


451

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;

อย่างไรก็ตามหากการดำเนินการแปลงข้อมูลยาวเกินไปจริง ๆ (MSDN ให้นิยามสิ่งนี้มากกว่า 2-7 วินาที) คุณควรใช้ตัวแสดงความคิดเห็นแบบเห็นภาพอื่นนอกเหนือจากเคอร์เซอร์เพื่อแจ้งให้ผู้ใช้ทราบถึงความคืบหน้า สำหรับการตั้งค่าเพิ่มเติมในเชิงลึกของแนวทางให้ดูที่บทความนี้

แก้ไข:
ตามที่ @Am ชี้ให้เห็นคุณอาจต้องโทรหาApplication.DoEvents();หลังจากนั้นCursor.Current = Cursors.WaitCursor;เพื่อให้แน่ใจว่านาฬิกาทรายแสดงขึ้นจริง


23
สิ่งนี้ไม่จำเป็นต้องเปลี่ยนเคอร์เซอร์ - หากไม่ได้วนลูปข้อความระหว่างรหัสที่ใช้เวลามาก เพื่อเปิดใช้งานคุณต้องเพิ่มApplication.DoEvents (); หลังเคอร์เซอร์ชุดแรก
Amirshk

16
คุณอาจต้องการลอง .. สุดท้ายบล็อกหลังจากการตั้งค่าปัจจุบันด้วย (ประกันว่าปัจจุบันได้รับการรีเซ็ตเป็นค่าเริ่มต้น)
TrueWill

7
FYI ฉันไม่สามารถทำงานได้ แต่เปลี่ยนเป็น this.cursor = cursors.waitcursor มันทำงานได้
Hans Rudel

4
นาฬิกาทรายไม่ปรากฏขึ้นหากฉันใช้ Application.DoEvents () หลังจาก Cursor.Current = Cursors.WaitCursor อย่างไรก็ตามมันทำงานได้โดยไม่ต้องใช้ Application.DoEvents () ไม่แน่ใจว่าทำไม
Vbp

14
เป็นการดีกว่าที่จะใช้Application.UseWaitCursor = trueและApplication.UseWaitCursor = false
Gianpiero

169

อันที่จริงแล้ว

Cursor.Current = Cursors.WaitCursor;

ตั้งค่าเคอร์เซอร์รอชั่วคราวแต่ไม่แน่ใจว่าเคอร์เซอร์รอจะปรากฏขึ้นจนกว่าจะสิ้นสุดการดำเนินการของคุณ โปรแกรมหรือตัวควบคุมอื่น ๆ ภายในโปรแกรมของคุณสามารถรีเซ็ตเคอร์เซอร์กลับเป็นลูกศรเริ่มต้นได้อย่างง่ายดายเนื่องจากในความเป็นจริงแล้วเกิดขึ้นเมื่อคุณเลื่อนเมาส์ขณะที่การทำงานยังคงทำงานอยู่

วิธีที่ดีกว่ามากในการแสดงเคอร์เซอร์รอคือการตั้งค่าคุณสมบัติ UseWaitCursor ในรูปแบบเป็นจริง:

form.UseWaitCursor = true;

สิ่งนี้จะแสดงเคอร์เซอร์รอสำหรับการควบคุมทั้งหมดในแบบฟอร์มจนกว่าคุณจะตั้งค่าคุณสมบัตินี้เป็นเท็จ หากคุณต้องการให้เคอร์เซอร์รอที่จะแสดงในระดับแอปพลิเคชันคุณควรใช้:

Application.UseWaitCursor = true;

ดีแล้วที่รู้. ผมพยายามที่จะทำเช่นเดียวกันใน WPF และจบลงด้วยเคอร์เซอร์ = Cursors.Waitและเคอร์เซอร์ = Cursors.Arrow แต่ฉันไม่พบ Cursor ภายใต้แอพ
itsho

2
ไม่พบ UseWaitCursor ในแอปพลิเคชัน!
จันทรา Eskay

ฉันพบว่าเมื่อตั้งค่า form.UseWaitCursor = false ในตอนท้ายของการดำเนินการจริง ๆ แล้วมันจะไม่รีเซ็ตเคอร์เซอร์จนกว่าคุณจะย้ายหรือคลิกเมาส์ OTOH ฟอร์มเคอร์เซอร์ไม่มีปัญหานี้ ฉันไม่สามารถรับเคอร์เซอร์ปัจจุบันทำงานได้เลย
สจ๊วต

39

การสร้างก่อนหน้านี้แนวทางที่ฉันชอบ (เนื่องจากนี่เป็นการกระทำที่ทำบ่อย) คือการห่อโค้ดเคอร์เซอร์รอในคลาสผู้ช่วย IDisposable ดังนั้นจึงสามารถใช้กับการใช้ () (รหัสหนึ่งบรรทัด) ใช้พารามิเตอร์ทางเลือกเรียกใช้ รหัสภายในจากนั้นล้างข้อมูล (คืนค่าเคอร์เซอร์) หลังจากนั้น

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

การใช้งาน:

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

แรงบันดาลใจจาก: codeproject.com/Articles/6287/WaitCursor-hack-using-using
torno

ไม่เคยเห็นแบบนั้น แต่ใช่วิธีการที่คล้ายกัน เขาสำรองเคอร์เซอร์ปัจจุบันแล้วคืนค่าซึ่งอาจเป็นประโยชน์หากคุณกำลังเปลี่ยนแปลงเคอร์เซอร์หนัก
mhapps

28

การใช้UseWaitCursorนั้นง่ายกว่าในระดับฟอร์มหรือหน้าต่าง กรณีการใช้งานทั่วไปสามารถมีลักษณะดังนี้:

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

เพื่อประสบการณ์ UI ที่ดีขึ้นคุณควรใช้ Asynchrony จากเธรดอื่น


2
นี่ควรเป็นคำตอบที่ยอมรับ มันเป็นเกมเดียวที่ใช้ลองได้ในที่สุด
John Henckel

1
มี upvote ของฉันฉันหายไปลองในที่สุดในการใช้งานของฉัน
แจ็ค

19

แนวทางของฉันคือการคำนวณทั้งหมดในผู้ทำงานเบื้องหลัง

จากนั้นเปลี่ยนเคอร์เซอร์ดังนี้:

this.Cursor = Cursors.Wait;

และในเหตุการณ์เสร็จสิ้นของเธรดจะคืนค่าเคอร์เซอร์:

this.Cursor = Cursors.Default;

หมายเหตุสิ่งนี้สามารถทำได้สำหรับการควบคุมเฉพาะดังนั้นเคอร์เซอร์จะเป็นนาฬิกาทรายเฉพาะเมื่อเมาส์อยู่เหนือสิ่งนั้น


@ มัลลิสต์: วิธีการที่ดี :) จากนั้นสิ่งที่คุณต้องทำคือการคืนค่าในเหตุการณ์สุดท้ายและสิ่งที่คุณทำ
Amirshk

4

ตกลงดังนั้นฉันจึงสร้างวิธีการซิงค์แบบคงที่ ที่ปิดใช้งานการควบคุมที่เรียกใช้การดำเนินการและเปลี่ยนเคอร์เซอร์แอปพลิเคชัน มันทำงานเป็นงานและรอให้เสร็จ การควบคุมกลับไปที่ผู้โทรในขณะที่รอ ดังนั้นแอปพลิเคชันยังคงตอบสนองแม้ในขณะที่ไอคอนไม่ว่างหมุน

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

นี่คือรูปแบบรหัสในรูปแบบหลัก

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

ฉันต้องใช้คนตัดไม้แยกต่างหากสำหรับการกระทำหุ่น (ฉันใช้ Nlog) และคนตัดไม้หลักของฉันกำลังเขียนถึง UI (กล่องข้อความที่หลากหลาย) ฉันไม่สามารถรับเคอร์เซอร์ไม่ว่างแสดงเฉพาะเมื่อผ่านคอนเทนเนอร์ที่เฉพาะเจาะจงในแบบฟอร์ม (แต่ฉันไม่ได้พยายามมาก) ตัวควบคุมทั้งหมดมีคุณสมบัติ UseWaitCursor แต่ดูเหมือนจะไม่มีผลใด ๆ กับตัวควบคุม ฉันลอง (อาจเป็นเพราะพวกเขาไม่ได้อยู่ด้านบน?)

นี่คือบันทึกหลักซึ่งแสดงสิ่งที่เกิดขึ้นตามลำดับที่เราคาดหวัง:

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

ด้วยชั้นเรียนด้านล่างคุณสามารถให้คำแนะนำเกี่ยวกับโดนัท "ข้อยกเว้นปลอดภัย"

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

CursorHandler คลาส

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 มุมมองของคนอื่นชัดเจนมาก แต่ฉันต้องการเพิ่มบางอย่างดังต่อไปนี้:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

2

สำหรับแอปพลิเคชัน Windows Forms การปิดใช้งานตัวเลือก UI-Control นั้นมีประโยชน์มาก ดังนั้นข้อเสนอแนะของฉันมีลักษณะเช่นนี้:

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

การใช้งาน:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.