커서를 대기 커서로 바꾸려면 어떻게해야합니까?


263

사용자가 로그인하는 C # 응용 프로그램이 있으며 해싱 알고리즘이 비싸기 때문에 시간이 조금 걸립니다. 사용자에게 대기 / 처리 커서 (일반적으로 모래 시계)를 표시하여 프로그램이 수행중인 작업을 알리는 방법은 무엇입니까?

프로젝트는 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
Current를 설정 한 후에 try..finally 블록을 원할 수도 있습니다 (Current가 Default로 재설정되도록 보장).
TrueWill

7
참고로, 위의 작업을 수행 할 수는 없지만 this.cursor = cursors.waitcursor; 효과가있었습니다.
Hans Rudel

4
Cursor.Current = Cursors.WaitCursor 다음에 Application.DoEvents ()를 사용한 경우 모래 시계가 표시되지 않았습니다. 그러나 Application.DoEvents ()없이 작동했습니다. 확실하지 않은 이유
Vbp

14
사용하는 것이 좋습니다 Application.UseWaitCursor = trueApplication.UseWaitCursor = false
Gianpiero

169

사실은,

Cursor.Current = Cursors.WaitCursor;

대기 커서를 일시적으로 설정하지만 작업이 끝날 때까지 대기 커서가 표시되지는 않습니다. 프로그램이 실행중인 동안 마우스를 움직일 때 발생하는 것처럼 프로그램 내의 다른 프로그램이나 컨트롤은 커서를 기본 화살표로 쉽게 다시 재설정 할 수 있습니다.

대기 커서를 표시하는 더 좋은 방법은 폼의 UseWaitCursor 속성을 true로 설정하는 것입니다.

form.UseWaitCursor = true;

이 속성을 false로 설정할 때까지 폼의 모든 컨트롤에 대한 대기 커서가 표시됩니다. 응용 프로그램 레벨에 대기 커서를 표시하려면 다음을 사용해야합니다.

Application.UseWaitCursor = true;

알아 둘만 한. WPF에서 동일한 작업을 시도하고 Cursor = Cursors.WaitCursor = Cursors.Arrow로 끝났습니다 . 하지만
itsho

2
응용 프로그램에서 UseWaitCursor를 찾을 수 없습니다!
Chandra Eskay

작업이 끝날 때 form.UseWaitCursor = false를 설정할 때 마우스를 움직이거나 클릭 할 때까지 실제로 커서가 재설정되지 않는다는 것을 알았습니다. OTOH, form.Cursor에는이 문제가 없습니다. Cursor.Current를 전혀 사용할 수 없었습니다.
스튜어트

39

이전에 구축 한 선호하는 접근 방식은 (자주 수행되는 작업이므로) 대기 커서 코드를 IDisposable 헬퍼 클래스로 래핑하여 using () (한 줄의 코드)과 함께 사용할 수 있으며 선택적 매개 변수를 사용하여 실행하는 것입니다. 코드를 입력 한 후 나중에 정리 (커서 복원)하십시오.

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
}


그것을 보지 못했지만 비슷한 접근 방식이 있습니다. 현재 커서를 백업 한 다음 복원합니다. 커서를 많이 변경하는 경우 유용 할 수 있습니다.
mhapps

27

폼 또는 창 수준에서 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
이것은 ACCEPTED 답변이어야합니다. try-finally를 사용하는 것은 유일합니다.
존 헨켈

1
내 공감대를 가지고, 구현에 마지막 시도가 누락되었습니다
Jack

19

내 접근 방식은 백그라운드 작업자에서 모든 계산을 수행하는 것입니다.

그런 다음 커서를 다음과 같이 변경하십시오.

this.Cursor = Cursors.Wait;

그리고 스레드의 종료 이벤트에서 커서를 복원하십시오.

this.Cursor = Cursors.Default;

특정 컨트롤에 대해서도이 작업을 수행 할 수 있으므로 마우스가 해당 컨트롤 위에있을 때만 모래 시계가됩니다.


@Malfist : 좋은 접근 방식 :), 그러면 최종 이벤트에 복원을 수행하고 완료하십시오.
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

좋아요, 다른 사람들의 견해는 매우 분명하지만 다음과 같이 몇 가지를 추가하고 싶습니다.

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.