实现进度条以显示正在完成的工作
本文关键字:工作 显示 实现 | 更新日期: 2023-09-27 18:00:12
我有一个表单Form1
,它包含两个元素按钮和进度条。当我点击按钮时,它会调用class.SomeFunction()
,然后它会从不同的类中调用一些其他函数来完成一些工作。
我想从class.SomeFunctin()
和SomeFunctino
调用的所有其他函数中增加进度条的值
class @class
{
public static void SomeFunction(var something)
{
progressbar.value++;
class2.Function2(something);
}
}
class class2
{
public static void Function2(var something)
{
progressbar.value++;
}
}
如何做到这一点?
你真的不应该让这些函数更新进度条——这违反了单一责任原则。您最好使用backgroundworker,或者在每次函数调用后从button_click事件中更新进度条。
如果你正在做一件需要很长时间才能显示进度条的事情,那么你应该在后台线程而不是表单中进行。这会使UI变得没有响应。
Code Project BackgroundWorker Thread的一篇文章中有一个后台线程的示例,它显示了WinForms应用程序中的进度条。
要做到这一点,最简单的方法就是调用在表单中处理的event
,并在事件处理程序中增加进度条。
您首先要做的是创建一个自定义EventArgs
。
public class ProgressEventArgs : EventArgs
{
public int Progress {get; set;}
public ProgressEventArgs(int progress)
{
Progress = progress;
}
}
然后,在您想要增加进度条的类中,您将想要引发此事件。
class Class2
{
public event EventHandler<ProgressEventArgs> ProgressEvent;
public void Function2(var something)
{
OnRaiseProgressEvent(new ProgressEventArgs(1));
}
protected virtual void OnRaiseProgressEvent(ProgressEventArgs e)
{
// C# 6 and above:
// Raise event if event handler is set (i.e. not null)
ProgressEvent?.Invoke(this, e);
// end C# >=6 code
// C# 5 and earlier:
EventHandler<ProgressEventArgs> handler = ProgressEvent;
if(handler != null)
{
//this is what actually raises the event.
handler(this, e);
}
// end C# <=5 code
}
}
然后在您的表格中,您将想要订阅活动
public class YourForm
{
public YourForm
{
Class2 yourClass2Instance = new Class2();
yourClass2Instance.ProgressEvent += ProgressEventHandler;
}
private void ProgressEventHandler(object sender, ProgressEventArgs e)
{
progressbar.Value += e.Progress;
}
}
在过去,我使用了一个简单的无菜单栏表单,上面有一个标签和Forms.ProgressBar,表单中使用以下代码:
public partial class ProgressDialog : Form
{
//public delegate delSetProgress
private readonly int progressBarMax;
/// <summary>
/// Structure used for passing progress bar related parameters as a single variable.
/// </summary>
public struct ProgressBarParams
{
public int value;
public string message;
public ProgressBarParams(string Message, int Value)
{
message = Message;
value = Value;
}
}
/// <summary>
/// Constructs the progress bar dialog and sets the progress bar's maximum value to maxValue.
/// </summary>
/// <param name="maxValue">Value to set to progress bar's Maximum property.</param>
public ProgressDialog(int maxValue)
{
InitializeComponent();
progressBarMax = maxValue;
}
private void ProgressDialog_Load(object sender, EventArgs e)
{
progressBar.Maximum = progressBarMax;
}
/// <summary>
/// Public method to update the progressDialog
/// </summary>
/// <param name="inputParams">Values to update on the progressDialog</param>
public void SetProgress(ProgressBarParams inputParams)
{
lblMessage.Text = inputParams.message;
progressBar.setValue(inputParams.value);
Update();
}
/// <summary>
/// This method should be called when the operation represented by the ProgressDialog is
/// completed. It shows an "operation complete" message for a second and then closes the form.
/// </summary>
public void Finish()
{
lblMessage.Text = "Operation complete.";
progressBar.setValue(progressBar.Maximum);
Update();
System.Threading.Thread.Sleep(1000);
this.Close();
}
}
public static class MyExtensions
{
/// <summary>
/// Implements a hack to get around a stupid rendering problem with the .NET progress bar in some environments.
/// Sets the progress bar value property.
/// </summary>
/// <param name="proBar">Progress bar control to set the value on.</param>
/// <param name="value">Value to be set.</param>
public static void setValue(this ProgressBar proBar, int value)
{
if (value > 0)
{
proBar.Value = value;
proBar.Value = value - 1;
proBar.Value = value;
}
else
{
proBar.Value = value;
proBar.Value = value + 1;
proBar.Value = value;
}
}
}
请注意setValue
扩展方法,该方法使用变通方法来避免某些版本的Windows出现问题。
然后我用以下内容设置它(和一个启动屏幕),其中m_frmProgress
是进度条形式:
// Create splash screen/progress bar thread on the thread pool
ThreadPool.QueueUserWorkItem((x) =>
{
bool localDone = false;
m_frmSplash.Show();
m_frmProgress.Show();
// Set the progress bar form up slightly below the bottom of the splash screen
m_frmProgress.Location = new Point(m_frmProgress.Location.X, m_frmProgress.Location.Y + (m_frmSplash.Height / 2) + (m_frmProgress.Height / 2) + 10);
while (!localDone) // Keep iterating until the main thread tells us we're done
{
lock (splashScreenDoneLock)
{
localDone = splashScreenDone;
}
// I don't like this method of keeping the UI responsive, but as yet I have no better method
Application.DoEvents();
Thread.Sleep(500);
}
m_frmSplash.Close();
m_frmProgress.Close();
while (!m_frmProgress.IsDisposed || !m_frmSplash.IsDisposed) // While either splash form is not disposed (still active)
{
Thread.Sleep(100); // Keep waiting until the splash forms are gone
}
splashFormsDisposed.Set(); // Signal the main thread that the splash forms are gone so the main form can be shown
});
bool isSplashHandleCreated = false;
bool isProgressHandleCreated = false;
// Wait until both splash forms are created
while (!(isSplashHandleCreated && isProgressHandleCreated))
{
lock (m_frmSplash)
{
isSplashHandleCreated = m_frmSplash.IsHandleCreated;
}
lock (m_frmProgress)
{
isProgressHandleCreated = m_frmProgress.IsHandleCreated;
}
Thread.Sleep(500);
}
并像这样调用它:
m_frmProgress.Invoke(new Action<ProgressDialog.ProgressBarParams>(m_frmProgress.SetProgress), progressLevel);
这不是最优雅的方法,但它在一个单独的线程上为你提供了一个干净的可更新进度条,当你处理它时,它会保持响应。我几乎是从一个正在工作的应用程序中复制和粘贴所有代码,所以它应该可以工作。另一方面,如果有任何不清楚的地方,我深表歉意。