ฉันมีแอปพลิเคชัน C # ที่มีผู้ใช้ที่ลงชื่อเข้าใช้และเนื่องจากอัลกอริทึมการแปลงแป้นพิมพ์มีราคาแพงจึงต้องใช้เวลาสักครู่จึงจะทำ ฉันจะแสดงเคอร์เซอร์ Wait / Busy (ปกติคือนาฬิกาทราย) ให้ผู้ใช้ทราบได้อย่างไรว่าโปรแกรมกำลังทำอะไรอยู่
โครงการอยู่ใน C #
ฉันมีแอปพลิเคชัน C # ที่มีผู้ใช้ที่ลงชื่อเข้าใช้และเนื่องจากอัลกอริทึมการแปลงแป้นพิมพ์มีราคาแพงจึงต้องใช้เวลาสักครู่จึงจะทำ ฉันจะแสดงเคอร์เซอร์ Wait / Busy (ปกติคือนาฬิกาทราย) ให้ผู้ใช้ทราบได้อย่างไรว่าโปรแกรมกำลังทำอะไรอยู่
โครงการอยู่ใน C #
คำตอบ:
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;
อย่างไรก็ตามหากการดำเนินการแปลงข้อมูลยาวเกินไปจริง ๆ (MSDN ให้นิยามสิ่งนี้มากกว่า 2-7 วินาที) คุณควรใช้ตัวแสดงความคิดเห็นแบบเห็นภาพอื่นนอกเหนือจากเคอร์เซอร์เพื่อแจ้งให้ผู้ใช้ทราบถึงความคืบหน้า สำหรับการตั้งค่าเพิ่มเติมในเชิงลึกของแนวทางให้ดูที่บทความนี้
แก้ไข:
ตามที่ @Am ชี้ให้เห็นคุณอาจต้องโทรหาApplication.DoEvents();
หลังจากนั้นCursor.Current = Cursors.WaitCursor;
เพื่อให้แน่ใจว่านาฬิกาทรายแสดงขึ้นจริง
Application.UseWaitCursor = true
และApplication.UseWaitCursor = false
อันที่จริงแล้ว
Cursor.Current = Cursors.WaitCursor;
ตั้งค่าเคอร์เซอร์รอชั่วคราวแต่ไม่แน่ใจว่าเคอร์เซอร์รอจะปรากฏขึ้นจนกว่าจะสิ้นสุดการดำเนินการของคุณ โปรแกรมหรือตัวควบคุมอื่น ๆ ภายในโปรแกรมของคุณสามารถรีเซ็ตเคอร์เซอร์กลับเป็นลูกศรเริ่มต้นได้อย่างง่ายดายเนื่องจากในความเป็นจริงแล้วเกิดขึ้นเมื่อคุณเลื่อนเมาส์ขณะที่การทำงานยังคงทำงานอยู่
วิธีที่ดีกว่ามากในการแสดงเคอร์เซอร์รอคือการตั้งค่าคุณสมบัติ UseWaitCursor ในรูปแบบเป็นจริง:
form.UseWaitCursor = true;
สิ่งนี้จะแสดงเคอร์เซอร์รอสำหรับการควบคุมทั้งหมดในแบบฟอร์มจนกว่าคุณจะตั้งค่าคุณสมบัตินี้เป็นเท็จ หากคุณต้องการให้เคอร์เซอร์รอที่จะแสดงในระดับแอปพลิเคชันคุณควรใช้:
Application.UseWaitCursor = true;
การสร้างก่อนหน้านี้แนวทางที่ฉันชอบ (เนื่องจากนี่เป็นการกระทำที่ทำบ่อย) คือการห่อโค้ดเคอร์เซอร์รอในคลาสผู้ช่วย IDisposable ดังนั้นจึงสามารถใช้กับการใช้ () (รหัสหนึ่งบรรทัด) ใช้พารามิเตอร์ทางเลือกเรียกใช้ รหัสภายในจากนั้นล้างข้อมูล (คืนค่าเคอร์เซอร์) หลังจากนั้น
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;
}
}
การใช้งาน:
using (new CursorWait())
{
// Perform some code that shows cursor
}
การใช้UseWaitCursorนั้นง่ายกว่าในระดับฟอร์มหรือหน้าต่าง กรณีการใช้งานทั่วไปสามารถมีลักษณะดังนี้:
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;
}
}
เพื่อประสบการณ์ UI ที่ดีขึ้นคุณควรใช้ Asynchrony จากเธรดอื่น
แนวทางของฉันคือการคำนวณทั้งหมดในผู้ทำงานเบื้องหลัง
จากนั้นเปลี่ยนเคอร์เซอร์ดังนี้:
this.Cursor = Cursors.Wait;
และในเหตุการณ์เสร็จสิ้นของเธรดจะคืนค่าเคอร์เซอร์:
this.Cursor = Cursors.Default;
หมายเหตุสิ่งนี้สามารถทำได้สำหรับการควบคุมเฉพาะดังนั้นเคอร์เซอร์จะเป็นนาฬิกาทรายเฉพาะเมื่อเมาส์อยู่เหนือสิ่งนั้น
ตกลงดังนั้นฉันจึงสร้างวิธีการซิงค์แบบคงที่ ที่ปิดใช้งานการควบคุมที่เรียกใช้การดำเนินการและเปลี่ยนเคอร์เซอร์แอปพลิเคชัน มันทำงานเป็นงานและรอให้เสร็จ การควบคุมกลับไปที่ผู้โทรในขณะที่รอ ดังนั้นแอปพลิเคชันยังคงตอบสนองแม้ในขณะที่ไอคอนไม่ว่างหมุน
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;
}
นี่คือรูปแบบรหัสในรูปแบบหลัก
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
{
}
}
ฉันต้องใช้คนตัดไม้แยกต่างหากสำหรับการกระทำหุ่น (ฉันใช้ Nlog) และคนตัดไม้หลักของฉันกำลังเขียนถึง UI (กล่องข้อความที่หลากหลาย) ฉันไม่สามารถรับเคอร์เซอร์ไม่ว่างแสดงเฉพาะเมื่อผ่านคอนเทนเนอร์ที่เฉพาะเจาะจงในแบบฟอร์ม (แต่ฉันไม่ได้พยายามมาก) ตัวควบคุมทั้งหมดมีคุณสมบัติ UseWaitCursor แต่ดูเหมือนจะไม่มีผลใด ๆ กับตัวควบคุม ฉันลอง (อาจเป็นเพราะพวกเขาไม่ได้อยู่ด้านบน?)
นี่คือบันทึกหลักซึ่งแสดงสิ่งที่เกิดขึ้นตามลำดับที่เราคาดหวัง:
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
ด้วยชั้นเรียนด้านล่างคุณสามารถให้คำแนะนำเกี่ยวกับโดนัท "ข้อยกเว้นปลอดภัย"
using (new CursorHandler())
{
// Execute your time-intensive hashing code here...
}
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;
}
Okey มุมมองของคนอื่นชัดเจนมาก แต่ฉันต้องการเพิ่มบางอย่างดังต่อไปนี้:
Cursor tempCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
//do Time-consuming Operations
Cursor.Current = tempCursor;
สำหรับแอปพลิเคชัน Windows Forms การปิดใช้งานตัวเลือก UI-Control นั้นมีประโยชน์มาก ดังนั้นข้อเสนอแนะของฉันมีลักษณะเช่นนี้:
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;
}
}
การใช้งาน:
private void UiControl_Click(object sender, EventArgs e)
{
using (new AppWaitCursor(sender))
{
LongRunningCall();
}
}
ใช้สิ่งนี้กับ WPF:
Cursor = Cursors.Wait;
// Your Heavy work here
Cursor = Cursors.Arrow;