实现进度条以显示正在完成的工作

本文关键字:工作 显示 实现 | 更新日期: 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);

这不是最优雅的方法,但它在一个单独的线程上为你提供了一个干净的可更新进度条,当你处理它时,它会保持响应。我几乎是从一个正在工作的应用程序中复制和粘贴所有代码,所以它应该可以工作。另一方面,如果有任何不清楚的地方,我深表歉意。