Подтвердить что ты не робот

Cursor.Current против this.Cursor

Есть ли разница между Cursor.Current и this.Cursor (где this является WinForm) в .Net? Я всегда использовал this.Cursor и мне очень повезло, но я недавно начал использовать CodeRush и только что вложил некоторый код в блок "Wait Cursor", а CodeRush использовал свойство Cursor.Current. Я видел в Интернете и на работе, где у других программистов были некоторые проблемы с свойством Cursor.Current. Мне просто хотелось узнать, есть ли разница в двух. Спасибо заранее.

Я сделал небольшой тест. У меня есть две winforms. Я нажимаю кнопку на form1, устанавливаю свойство Cursor.Current на Cursors.WaitCursor, а затем показываю form2. Курсор не изменяется в любой форме. Он остается курсором Cursors.Default (указатель).

Если я установил this.Cursor в Cursors.WaitCursor в событии нажатия кнопки на form1 и покажу form2, курсор ожидания будет отображаться только на form1, а курсор по умолчанию - на form2, который ожидается. Итак, я до сих пор не знаю, что делает Cursor.Current.

4b9b3361

Ответ 1

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);
  }
}

Ответ 2

Я считаю, что Cursor.Current является используемым курсором мыши (независимо от того, где он находится на экране), в то время как this.Cursor - это курсор, на который он будет установлен, когда мышь пройдет через ваше окно.

Ответ 3

На самом деле, если вы хотите использовать HourGlass из другого потока, который даст вам обратное перекрестное потоковое исключение, потому что вы пытаетесь получить доступ к f.Handle из другого потока, чем форма была изначально создана. Вместо этого используйте функцию GetForegroundWindow() вместо user32.dll.

[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);
    }
}

Ответ 4

this.Cursor - это курсор, который будет использоваться, когда мышь находится над окном, на которое указывает this. Cursor.Current - текущий курсор мыши, который может отличаться от this.Cursor, если мышь находится в другом окне.

Ответ 5

Я заметил интересную вещь о настройке курсоров, поэтому я хотел бы разъяснить некоторые недоразумения, которые у меня были раньше, и я надеюсь, что это может помочь и другим:

При попытке установить курсор формы с помощью

this.cursor = Cursors.Waitcursor

вы на самом деле устанавливаете курсор для элемента управления, а не для всей формы, поскольку курсор является свойством класса Control.

Также курсор будет только изменен на данный курсор, когда мышь фактически находится над фактическим элементом управления (явно область формы)

Как сказал Ханс Пассант,

Windows отправляет окно, содержащее курсор мыши, Сообщение WM_SETCURSOR, дающее возможность изменить курсор форма

Я не знаю, будут ли окна отправлять сообщения непосредственно элементам управления или если форма передает эти сообщения ему дочерних элементов управления на основе положения мыши, я, скорее всего, догадываюсь о первом методе, поскольку, когда я получал сообщения с переопределением WndProc управления формой, когда я был над текстовым полем, например, форма не обрабатывала никаких сообщений. (пожалуйста, кто-то дает ясность в этом)

В принципе, мое предложение состояло бы в том, чтобы жить от этого.курсора и придерживаться this.usewaitcursor, так как это меняет свойство cursor на waitcursor для всех дочерних элементов управления.

Проблема с этим тоже такая же, как и на уровне приложения Application.usewaitcursor, в то время как вы не над формой/формами с вашим курсором, никакое сообщение WM_SETCURSOR не отправляется окнами, поэтому, если вы начнете длительную синхронную операцию перед перемещением мыши над областью формы форма может обрабатывать только такое сообщение, когда заканчивается длительная синхронная операция.

(я бы не предлагал запускать много времени в потоке пользовательского интерфейса вообще, в основном это то, что вызывает проблему здесь)

Я немного улучшил ответ Ханса Пассана, поэтому песочные часы можно установить либо на уровне приложения, либо на уровне формы, а также избегать 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);
}

и использовать его позже в блоке finally catch try

Ответ 6

Это отлично работает для меня, когда LongRunningOperation() обрабатывает сообщения.

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

Ответ 7

От VB.net VS 2012

Windows.Forms.Cursor.Current = Cursors.Default