programing

Cursor.Current vs. this. Cursor

nasanasas 2021. 1. 11. 08:20
반응형

Cursor.Current vs. this. Cursor


사이에 차이가 있나요 Cursor.Currentthis.Cursor(여기서 this.NET에서의 WinForm입니다)? 저는 항상 사용 this.Cursor해왔고 매우 행운이 있었지만 최근에 CodeRush를 사용하기 시작했고 "Wait Cursor"블록에 일부 코드를 삽입했고 CodeRush는이 Cursor.Current속성을 사용 했습니다. 나는 인터넷과 직장에서 다른 프로그래머가 Cursor.Current부동산에 문제가있는 것을 보았다 . 둘 사이에 차이가 있는지 궁금해졌습니다. 미리 감사드립니다.

나는 약간의 테스트를했다. 두 개의 winform이 있습니다. form1의 버튼을 클릭하고 Cursor.Current속성을로 설정 Cursors.WaitCursor한 다음 form2를 표시합니다. 커서는 두 양식 모두에서 변경되지 않습니다. Cursors.Default(포인터) 커서로 남아 있습니다 .

form1의 버튼 클릭 이벤트에서로 설정 this.Cursor하고 Cursors.WaitCursorform2를 표시하면 대기 커서는 form1에만 표시되고 기본 커서는 예상되는 form2에 있습니다. 그래서 나는 아직도 무엇을하는지 모른다 Cursor.Current.


Windows는 마우스 커서가 포함 된 창에 WM_SETCURSOR 메시지를 보내 커서 모양을 변경할 수있는 기회를 제공합니다. TextBox와 같은 컨트롤은이를 활용하여 커서를 I 바로 변경합니다. Control.Cursor 속성은 사용할 모양을 결정합니다.

Cursor.Current 속성은 WM_SETCURSOR 응답을 기다리지 않고 모양을 직접 변경합니다. 대부분의 경우 그 모양은 오래 지속되지 않을 것입니다. 사용자가 마우스를 움직이면 WM_SETCURSOR는 마우스를 Control.Cursor로 다시 변경합니다.

UseWaitCursor 속성은 모래 시계를 더 쉽게 표시 할 수 있도록 .NET 2.0에 추가되었습니다. 불행히도 잘 작동하지 않습니다. 모양을 변경하려면 WM_SETCURSOR 메시지가 필요하며 속성을 true로 설정 한 다음 시간이 걸리는 작업을 수행하면 발생하지 않습니다. 예를 들어 다음 코드를 시도하십시오.

private void button1_Click(object sender, EventArgs e) {
  this.UseWaitCursor = true;
  System.Threading.Thread.Sleep(3000);
  this.UseWaitCursor = false;
}

커서는 변경되지 않습니다. 모양을 만들려면 Cursor.Current도 사용해야합니다. 다음은 쉽게 할 수있는 작은 도우미 클래스입니다.

using System;
using System.Windows.Forms;

public class HourGlass : IDisposable {
  public HourGlass() {
    Enabled = true;
  }
  public void Dispose() {
    Enabled = false;
  }
  public static bool Enabled {
    get { return Application.UseWaitCursor; }
    set {
      if (value == Application.UseWaitCursor) return;
      Application.UseWaitCursor = value;
      Form f = Form.ActiveForm;
      if (f != null && f.Handle != IntPtr.Zero)   // Send WM_SETCURSOR
        SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1);
    }
  }
  [System.Runtime.InteropServices.DllImport("user32.dll")]
  private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

다음과 같이 사용하십시오.

private void button1_Click(object sender, EventArgs e) {
  using (new HourGlass()) {
    System.Threading.Thread.Sleep(3000);
  }
}

Cursor.Current는 현재 사용중인 마우스 커서 (화면의 위치에 관계없이)라고 생각하지만 this.Cursor는 마우스가 창을 지나갈 때 설정 될 커서입니다.


this.Cursor에서 참조하는 창 위에 마우스를 놓을 때 사용되는 커서입니다 this. Cursor.Current현재 마우스 커서로, this.Cursor마우스가 다른 창 위에있는 경우와 다를 수 있습니다.


실제로 다른 스레드에서 HourGlass를 사용하려는 경우 원래 양식이 생성 된 것과 다른 스레드에서 f.Handle에 액세스하려고하기 때문에 크로스 스레딩 예외를 다시 제공합니다. user32.dll 대신 GetForegroundWindow ()를 사용하십시오.

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

그리고

public static bool Enabled
{
    get
    {
        return Application.UseWaitCursor;
    }

    set
    {
        if (value == Application.UseWaitCursor)
        {
            return;
        }

        Application.UseWaitCursor = value;
        var handle = GetForegroundWindow();
        SendMessage(handle, 0x20, handle, (IntPtr)1);
    }
}

커서 설정에 대해 흥미로운 점을 발견 했으므로 이전에 나 자신이 가졌던 오해를 제거하고 다른 사람들에게도 도움이되기를 바랍니다.

사용하여 양식의 커서를 설정하려고 할 때

this.cursor = 커서. 웨이트 커서

커서는 Control 클래스의 속성이기 때문에 전체 폼이 아닌 컨트롤에 대한 커서를 실제로 설정합니다.

또한 마우스가 실제로 실제 컨트롤 위에있을 때 (명시 적으로 폼의 영역) 커서는 주어진 커서로만 변경됩니다.

Hans Passant는 이미 다음과 같이 말했습니다.

Windows는 마우스 커서가있는 창에 WM_SETCURSOR 메시지를 보내 커서 모양을 변경할 수있는 기회를 제공합니다.

Windows가 컨트롤에 직접 메시지를 보내는 지 또는 양식이 마우스 위치에 따라 해당 메시지를 자식 컨트롤에 릴레이하는지 여부를 모르겠습니다. 양식의 WndProc를 재정 의하여 메시지를 가져올 때부터 첫 번째 방법을 추측 할 것입니다. 컨트롤, 예를 들어 텍스트 상자 위에있을 때 양식이 메시지를 처리하지 않았습니다. (누군가 이것에 대해 명확하게 알려주십시오)

기본적으로 내 제안은 this.cursor를 사용하여 거주하고 this.usewaitcursor를 고수하는 것입니다. 모든 자식 컨트롤에 대해 커서 속성이 waitcursor로 변경되기 때문입니다.

이것의 문제는 또한 응용 프로그램 수준 Application.usewaitcursor와 동일하지만 커서로 양식 / 양식 위에 있지 않은 동안 WM_SETCURSOR 메시지가 창에서 전송되지 않으므로 이동하기 전에 시간이 많이 걸리는 동기 작업을 시작하면 양식 영역 위에 마우스를 놓으면 시간이 많이 걸리는 동기 작업이 완료 될 때만 양식에서 이러한 메시지를 처리 ​​할 수 ​​있습니다.

(나는 UI 스레드에서 시간이 많이 걸리는 작업을 전혀 실행하지 않는 것이 좋습니다. 주로 이것이 여기서 문제를 일으키는 원인입니다)

Hans Passant의 답변을 약간 개선 했으므로 모래 시계는 응용 프로그램 수준 또는 양식 수준에서 설정할 수 있으며 교차 스레드 작업 호출에서 InvalidOperationException을 피할 수도 있습니다.

using System;
using System.Windows.Forms;

public class HourGlass : IDisposable
{
    public static bool ApplicationEnabled
    {
        get{ return Application.UseWaitCursor; }
        set
        {
            Form activeFrom = Form.ActiveForm;
            if (activeFrom == null || ApplicationEnabled == value) return;
            if (ApplicationEnabled == value)return;
            Application.UseWaitCursor = (bool)value;

            if (activeFrom.InvokeRequired)
            {
                activeFrom.BeginInvoke(new Action(() =>
                {
                    if (activeFrom.Handle != IntPtr.Zero)
                    SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
                }));
            }
            else
            {
                if (activeFrom.Handle != IntPtr.Zero)
                SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
            }
        }
    }

    private Form f;

    public HourGlass() 
    {
        this.f = Form.ActiveForm;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = true;
    }

    public HourGlass(bool enabled)
    {
        this.f = Form.ActiveForm;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = enabled;
    }

    public HourGlass(Form f, bool enabled)
    {
        this.f = f;

        if (f == null)
        {
            throw new ArgumentException();
        }
        Enabled = enabled;
    }

    public HourGlass(Form f)
    {
        this.f = f;

        if (f == null)
        {
            throw new ArgumentException();
        }

        Enabled = true;
    }

    public void Dispose()
    {
        Enabled = false;
    }

    public bool Enabled
    {
        get { return f.UseWaitCursor; }
        set
        {
            if (f == null || Enabled == value) return;
            if (Application.UseWaitCursor == true && value == false) return;

            f.UseWaitCursor = (bool)value;

            if(f.InvokeRequired)
            {
                f.BeginInvoke(new Action(()=>
                {
                    if (f.Handle != IntPtr.Zero)
                    SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
                }));
            }
            else
            {
                if (f.Handle != IntPtr.Zero)
                SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
            }
        }
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

응용 프로그램 수준에서 사용하려면 :

try
{
  HourGlass.ApplicationEnabled = true;
  //time consuming synchronous task
}
finally
{
  HourGlass.ApplicationEnabled = false;
}

양식 수준에서 사용하려면 현재 활성 양식에 사용할 수 있습니다.

using (new HourGlass())
{
  //time consuming synchronous task
}

또는 다음과 같은 형식으로 지역 변수를 초기화 할 수 있습니다.

public readonly HourGlass hourglass;

public Form1()
{
    InitializeComponent();
    hourglass = new HourGlass(this, false);
}

나중에 try catch finally 블록에서 사용


이것은 LongRunningOperation ()이 메시지를 처리 ​​할 때 잘 작동합니다.

private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    LongRunningOperation();
    this.Cursor = Cursors.Arrow;
}

VB.net VS 2012에서

Windows.Forms.Cursor.Current = Cursors.Default

참조 URL : https://stackoverflow.com/questions/302663/cursor-current-vs-this-cursor

반응형