.NetでCursor.Current
とthis.Cursor
(this
はWinForm)に違いはありますか?私は常にthis.Cursor
を使用しており、非常に幸運でしたが、最近CodeRushの使用を開始し、「Wait Cursor」ブロックにコードを埋め込み、CodeRushはCursor.Current
プロパティを使用しました。私はインターネットで、そして他のプログラマーがCursor.Current
プロパティに関していくつかの問題を抱えている職場で見ました。 2つに違いがあるのではないかと思うようになりました。前もって感謝します。
私は少しテストをしました。 2つのwinformがあります。 form1のボタンをクリックして、Cursor.Current
プロパティをCursors.WaitCursor
に設定し、form2を表示します。どちらのフォームでもカーソルは変わりません。 Cursors.Default
(ポインター)カーソルのままです。
Form1のボタンクリックイベントでthis.Cursor
をCursors.WaitCursor
に設定してform2を表示すると、待機カーソルは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はマウスがウィンドウ上を通過するときに設定されるカーソルだと思います。
実際に、フォームが最初に作成されたものとは異なるスレッドから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);
}
}
this.Cursor
は、this
によって参照されるウィンドウ上にマウスが置かれたときに使用されるカーソルです。 Cursor.Current
は現在のマウスカーソルです。マウスが別のウィンドウ上にある場合、this.Cursor
とは異なる場合があります。
カーソルの設定に関する興味深いことに気づいたので、私自身が以前持っていたいくつかの誤解を解消したいと思います。
を使用してフォームのカーソルを設定しようとすると
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ブロックで使用します
VB.net VS 2012から
Windows.Forms.Cursor.Current = Cursors.Default
これは、LongRunningOperation()がメッセージを処理している場合に非常に役立ちます。
private void btnDoLongRunningOperation_Click(object sender, System.EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
LongRunningOperation();
this.Cursor = Cursors.Arrow;
}