web-dev-qa-db-ja.com

Cursor.Currentとthis.Cursor

.NetでCursor.Currentthis.CursorthisはWinForm)に違いはありますか?私は常にthis.Cursorを使用しており、非常に幸運でしたが、最近CodeRushの使用を開始し、「Wait Cursor」ブロックにコードを埋め込み、CodeRushはCursor.Currentプロパティを使用しました。私はインターネットで、そして他のプログラマーがCursor.Currentプロパティに関していくつかの問題を抱えている職場で見ました。 2つに違いがあるのではないかと思うようになりました。前もって感謝します。

私は少しテストをしました。 2つのwinformがあります。 form1のボタンをクリックして、Cursor.CurrentプロパティをCursors.WaitCursorに設定し、form2を表示します。どちらのフォームでもカーソルは変わりません。 Cursors.Default(ポインター)カーソルのままです。

Form1のボタンクリックイベントでthis.CursorCursors.WaitCursorに設定してform2を表示すると、待機カーソルはform1にのみ表示され、デフォルトのカーソルはform2にあります。したがって、Cursor.Currentが何をするのかはまだわかりません。

54
Keith

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);
  }
}
87
Hans Passant

Cursor.Currentは現在使用されているマウスカーソル(画面上の場所に関係なく)であり、this.Cursorはマウスがウィンドウ上を通過するときに設定されるカーソルだと思います。

10
James Curran

実際に、フォームが最初に作成されたものとは異なるスレッドからf.Handleにアクセスしようとしているため、クロススレッド例外を返す別のスレッドからHourGlassを使用する場合。代わりに、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);
    }
}
6

this.Cursorは、thisによって参照されるウィンドウ上にマウスが置かれたときに使用されるカーソルです。 Cursor.Currentは現在のマウスカーソルです。マウスが別のウィンドウ上にある場合、this.Cursorとは異なる場合があります。

6
Joel Coehoorn

カーソルの設定に関する興味深いことに気づいたので、私自身が以前持っていたいくつかの誤解を解消したいと思います。

を使用してフォームのカーソルを設定しようとすると

this.cursor = Cursors.Waitcursor

カーソルはControlクラスのプロパティであるため、実際にはフォーム全体ではなくコントロールにカーソルを設定します。

もちろん、実際にマウスが実際のコントロール(明示的にフォームの領域)の上にある場合にのみ、カーソルは指定されたカーソルに変更されます

ハンス・パッサントがすでに述べているように:

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ブロックで使用します

2
Attila Horváth

VB.net VS 2012から

Windows.Forms.Cursor.Current = Cursors.Default
0
Elian

これは、LongRunningOperation()がメッセージを処理している場合に非常に役立ちます。

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