57

Cursor.Currentthis.Cursor( thisWinForm はどこにあるのか) に違いはありますか? 私は常にそれを使用this.Cursorしており、非常に幸運でしたが、最近 CodeRush の使用を開始し、「Wait Cursor」ブロックにコードを埋め込んだだけで、CodeRush はCursor.Currentプロパティを使用しました。Cursor.Currentインターネットや職場で、他のプログラマーがプロパティに問題を抱えているのを見てきました。2つに違いがあるのか​​ 疑問に思っただけです。前もって感謝します。

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

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

4

7 に答える 7

90

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);
  }
}
于 2008-11-19T18:23:19.190 に答える
11

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

于 2008-11-19T17:19:32.750 に答える
6

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

于 2008-11-19T17:20:35.070 に答える
6

実際、フォームが最初に作成されたスレッドとは異なるスレッドから 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);
    }
}
于 2011-12-15T05:45:26.133 に答える
3

カーソルの設定について興味深いことに気づいたので、私自身が以前持っていたいくつかの誤解を解消したいと思います。それが他の人にも役立つことを願っています:

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

this.cursor = Cursors.Waitcursor

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

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

Hans Passant がすでに述べているように、

Windows は、マウス カーソルを含むウィンドウに WM_SETCURSOR メッセージを送信し、カーソルの形状を変更する機会を与えます。

Windowsがメッセージをコントロールに直接送信するのか、フォームがマウスの位置に基づいてそれらのメッセージを子コントロールに中継するのかはわかりません。フォームのWndProcをオーバーライドしてメッセージをフェッチしたときから、最初の方法を推測する可能性が最も高いでしょう。たとえば、テキストボックスの上にいるとき、フォームはメッセージを処理しませんでした。(誰かこれについて明確にしてください)

基本的に私の提案は、this.cursor も使用しないようにし、this.usewaitcursor に固執することです。これは、すべての子コントロールのカーソル プロパティを waitcursor に変更するためです。

これに関する問題は、アプリケーション レベルの Application.usewaitcursor と同じです。フォーム上にカーソルを置いていない間は、Windows から 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 ブロックで使用します

于 2016-04-30T20:29:06.413 に答える
0

これは、LongRunningOperation() がメッセージを処理しているときにうまく機能します。

private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    LongRunningOperation();
    this.Cursor = Cursors.Arrow;
}
于 2015-02-15T03:40:49.723 に答える