Odpowiedzi:
Możesz użyć Cursor.Current
.
// Set cursor as hourglass
Cursor.Current = Cursors.WaitCursor;
// Execute your time-intensive hashing code here...
// Set cursor as default arrow
Cursor.Current = Cursors.Default;
Jeśli jednak operacja mieszania jest naprawdę długa (MSDN określa to jako ponad 2-7 sekund), prawdopodobnie powinieneś użyć wizualnego wskaźnika sprzężenia zwrotnego innego niż kursor, aby powiadomić użytkownika o postępie. Bardziej szczegółowy zestaw wytycznych znajduje się w tym artykule .
Edycja:
Jak wskazał @Am, może być konieczne zadzwonienie Application.DoEvents();
po, Cursor.Current = Cursors.WaitCursor;
aby upewnić się, że klepsydra jest rzeczywiście wyświetlana.
Application.UseWaitCursor = true
iApplication.UseWaitCursor = false
Tak właściwie,
Cursor.Current = Cursors.WaitCursor;
tymczasowo ustawia kursor Oczekiwanie, ale nie zapewnia, że kursor Oczekiwanie będzie wyświetlany do końca operacji. Inne programy lub elementy sterujące w twoim programie mogą łatwo zresetować kursor z powrotem do domyślnej strzałki, co w rzeczywistości dzieje się, gdy poruszasz myszą, gdy operacja jest nadal uruchomiona.
O wiele lepszym sposobem pokazania kursora Wait jest ustawienie właściwości UseWaitCursor w formie na true:
form.UseWaitCursor = true;
Spowoduje to wyświetlenie kursora oczekiwania dla wszystkich kontrolek w formularzu, dopóki ta właściwość nie zostanie ustawiona na false. Jeśli chcesz, aby kursor był wyświetlany na poziomie aplikacji, powinieneś użyć:
Application.UseWaitCursor = true;
Opierając się na poprzednim, moim preferowanym podejściem (ponieważ jest to często wykonywana czynność) jest zawinięcie kodu kursora oczekiwania w klasę pomocniczą IDisposable, aby można go było używać za pomocą () (jeden wiersz kodu), brać parametry opcjonalne, uruchamiać kod wewnątrz, a następnie wyczyść (przywróć kursor).
public class CursorWait : IDisposable
{
public CursorWait(bool appStarting = false, bool applicationCursor = false)
{
// Wait
Cursor.Current = appStarting ? Cursors.AppStarting : Cursors.WaitCursor;
if (applicationCursor) Application.UseWaitCursor = true;
}
public void Dispose()
{
// Reset
Cursor.Current = Cursors.Default;
Application.UseWaitCursor = false;
}
}
Stosowanie:
using (new CursorWait())
{
// Perform some code that shows cursor
}
Użycie UseWaitCursor jest łatwiejsze na poziomie formularza lub okna. Typowy przypadek użycia może wyglądać jak poniżej:
private void button1_Click(object sender, EventArgs e)
{
try
{
this.Enabled = false;//optional, better target a panel or specific controls
this.UseWaitCursor = true;//from the Form/Window instance
Application.DoEvents();//messages pumped to update controls
//execute a lengthy blocking operation here,
//bla bla ....
}
finally
{
this.Enabled = true;//optional
this.UseWaitCursor = false;
}
}
Aby uzyskać lepszy interfejs użytkownika, należy użyć asynchronii z innego wątku.
Moje podejście polegałoby na tym, aby wszystkie obliczenia były przetwarzane w tle.
Następnie zmień kursor w następujący sposób:
this.Cursor = Cursors.Wait;
W zdarzeniu zakończenia wątku przywróć kursor:
this.Cursor = Cursors.Default;
Uwaga: można to również zrobić w przypadku określonych elementów sterujących, więc kursor będzie klepsydrą tylko wtedy, gdy mysz znajdzie się nad nimi.
OK, więc utworzyłem statyczną metodę asynchroniczną. Spowodowało to wyłączenie kontrolki uruchamiającej akcję i zmieniającej kursor aplikacji. Uruchamia akcję jako zadanie i czeka na zakończenie. Kontrola wraca do dzwoniącego podczas oczekiwania. Dzięki temu aplikacja pozostaje responsywna, nawet gdy ikona zajętości się obraca.
async public static void LengthyOperation(Control control, Action action)
{
try
{
control.Enabled = false;
Application.UseWaitCursor = true;
Task doWork = new Task(() => action(), TaskCreationOptions.LongRunning);
Log.Info("Task Start");
doWork.Start();
Log.Info("Before Await");
await doWork;
Log.Info("After await");
}
finally
{
Log.Info("Finally");
Application.UseWaitCursor = false;
control.Enabled = true;
}
Oto kod z formularza głównego
private void btnSleep_Click(object sender, EventArgs e)
{
var control = sender as Control;
if (control != null)
{
Log.Info("Launching lengthy operation...");
CursorWait.LengthyOperation(control, () => DummyAction());
Log.Info("...Lengthy operation launched.");
}
}
private void DummyAction()
{
try
{
var _log = NLog.LogManager.GetLogger("TmpLogger");
_log.Info("Action - Sleep");
TimeSpan sleep = new TimeSpan(0, 0, 16);
Thread.Sleep(sleep);
_log.Info("Action - Wakeup");
}
finally
{
}
}
Musiałem użyć osobnego programu rejestrującego dla akcji fikcyjnej (korzystam z Nlog), a mój główny program rejestrujący pisze w interfejsie użytkownika (bogate pole tekstowe). Nie byłem w stanie wyświetlić zajętego kursora tylko wtedy, gdy nad konkretnym kontenerem w formularzu (ale nie bardzo się starałem). Wszystkie kontrolki mają właściwość UseWaitCursor, ale nie ma to żadnego wpływu na kontrolki Próbowałem (może dlatego, że nie były na topie?)
Oto główny dziennik, który pokazuje, co dzieje się w oczekiwanej kolejności:
16:51:33.1064 Launching lengthy operation...
16:51:33.1215 Task Start
16:51:33.1215 Before Await
16:51:33.1215 ...Lengthy operation launched.
16:51:49.1276 After await
16:51:49.1537 Finally
Korzystając z poniższej klasy, możesz uczynić sugestię dotyczącą pączka „wyjątkowym bezpiecznym”.
using (new CursorHandler())
{
// Execute your time-intensive hashing code here...
}
klasa CursorHandler
public class CursorHandler
: IDisposable
{
public CursorHandler(Cursor cursor = null)
{
_saved = Cursor.Current;
Cursor.Current = cursor ?? Cursors.WaitCursor;
}
public void Dispose()
{
if (_saved != null)
{
Cursor.Current = _saved;
_saved = null;
}
}
private Cursor _saved;
}
W przypadku aplikacji Windows Forms bardzo przydatne może być opcjonalne wyłączenie interfejsu użytkownika. Więc moja sugestia wygląda następująco:
public class AppWaitCursor : IDisposable
{
private readonly Control _eventControl;
public AppWaitCursor(object eventSender = null)
{
_eventControl = eventSender as Control;
if (_eventControl != null)
_eventControl.Enabled = false;
Application.UseWaitCursor = true;
Application.DoEvents();
}
public void Dispose()
{
if (_eventControl != null)
_eventControl.Enabled = true;
Cursor.Current = Cursors.Default;
Application.UseWaitCursor = false;
}
}
Stosowanie:
private void UiControl_Click(object sender, EventArgs e)
{
using (new AppWaitCursor(sender))
{
LongRunningCall();
}
}
Użyj tego z WPF:
Cursor = Cursors.Wait;
// Your Heavy work here
Cursor = Cursors.Arrow;