问题
Do you have a good solution for keeping a 'Please wait' winform 'painted' while an application is performing a long running task ?
I've tried using form.refresh() on each step, but there are a few long running queries that take place, which means that this isn't frequent enough.
Basically this SO Question but, on C# in Excel through VSTO (rather than Python).
回答1:
Here's a good example with code in C#.
It is for splash screens specifically, however it is almost identical process (perhaps minus some of the flashy Opacity) to create a Please wait window.
The key information is that you will need a separate thread. This can complicate things however the article gives a good coverage/example of how to do it correctly.
回答2:
As statichippo mentioned, I would use the BackgroundWorker Class. Its purpose is to simplify multi-threading and allow for a worker thread to do the time consuming processing without locking the GUI.
Here is a quote from MSDN:
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
Here is a good tutorial how to use the BackgroundWorker class in windows forms: Implementing multi-threading in WinForms using the BackgroundWorker class
There are more complicated ways to implement Multi-Threading in C# for complex scenarios but for most simple scenarios the BackgroundWorker works great (for me at least).
Here are some links I pulled from Google on C# Multi Threading:
MSDN Threading
Introduction to Multithreading in C#
回答3:
Another option is to use an asynchronous delegate to show the form on a threadpool thread.
Threads in the threadpool are recommended for shorter lived threads that do not last for the entire application duration. As this is to display a short-lived please wait window, the threadpool is a reasonable choice.
The Action delegate (.NET 2.0+) is used along with it's BeginInvoke() method to automatically run the delegate code on a threadpool thread.
Some notes:
- It is important to use Control.BeginInvoke for any cross thread GUI calls, such as closing the please wait form in ClosePleaseWait().
- Also, the
m_pleaseWaitForm.ShowDialog();
actually starts a new message loop in the new thread. This is what keeps the please wait form alive. - Because a threadpool thread is used, this thread is automatically a background thread and will be terminated if the main application is closed.
- Apart from running on another thread, there is nothing special about Form2. You can place any child controls such as Pictureboxes, labels etc on it.
(MethodInvoker)delegate { ... }
is just a .NET 2.0 way of running code in a delegate inline.
The example below can be added to a WinForms project containing Form1: the main form, and Form2: the please wait form.
private Form2 m_pleaseWaitForm = null;
private void Form1_Shown(object sender, EventArgs e)
{
// This code could also be placed in eg. a button click event handler.
Action<Rectangle> a = new Action<Rectangle>(ShowPleaseWait);
a.BeginInvoke(this.Bounds, null, null);
// Do your long running tasks
ClosePleaseWait();
}
private void ShowPleaseWait(Rectangle bounds)
{
// This method runs on the new thread.
m_pleaseWaitForm = new Form2();
m_pleaseWaitForm.TopMost = true;
m_pleaseWaitForm.Location = new Point(bounds.Left + bounds.Width / 2 - m_pleaseWaitForm.Width / 2, bounds.Top + bounds.Height / 2 - m_pleaseWaitForm.Height / 2);
m_pleaseWaitForm.ShowDialog();
}
private void ClosePleaseWait()
{
m_pleaseWaitForm.BeginInvoke((MethodInvoker)delegate { m_pleaseWaitForm.Close(); });
}
回答4:
Use a BackgroundWorker
来源:https://stackoverflow.com/questions/2094295/winform-updates-on-long-running-tasks