问题
I have a long running method, so I created a progress bar to show what percentage my method was to completing, but I am having difficulty figuring out how to do this ,syncing my progress bar with my method => excelHelper.InsertNewRows();.
public void ButtonSubmit_Click(object sender, EventArgs e)
{
if (isProcessRunning)
{
MessageBox.Show("A Process is aleady running");
return;
}
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
for (int n = 0; n < 100; n++)
{
isProcessRunning = true;
Thread.Sleep(50);
progressBar1.Invoke(
new Action(() =>
{
progressBar1.Value = n;
label3.Text = ("Progress: " + n + "%");
}
));
}
MessageBox.Show("Thread completed!");
progressBar1.Invoke(
new Action(() =>
{
progressBar1.Value = 0;
}
));
isProcessRunning = false;
}
));
backgroundThread.Start();
excelHelper.InsertNewRows();
var folder = TextOutputFile.Text + @"\" +
DateTime.Now.ToString("yyyy_MM_dd_") + "SA_Analysis_Report.xlsx";
excelHelper.Save(folder);
MessageBox.Show("File has been added to file");
}
回答1:
IMO your problem is, that you dont have any communication between the working thread excelHelper.InsertNewRows();
and your progressbar thread. Both threads are running without any information about the other thread and their progress.
You could rewrite the background thread, so its capable of taking the percentage as a parameter, which is shown in the moment you call the thread.
E.g. Pseudo Code:
public void progressbarThread(int percentage)
{
// Invoke the percentage to your progressbar, then terminate this thread
}
public void InsertNewRows()
{
// Do something...
// 10%
Thread backgroundThread = new Thread(new ThreadStart(() => progressBarThread(10)));
backgroundThread.Start();
// Do something...
// 50%
Thread backgroundThread = new Thread(new ThreadStart(() => progressBarThread(50)));
backgroundThread.Start();
// etc. etc.
}
Update: I've found this on my own research how to build a smooth loadingbar with an extra form, it maybe useful: Form in an extra Thread
回答2:
I use this form class: -
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Yournamespace
{
public partial class frmProgressBar : Form
{
public Action Worker { get; set; }
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
public const long MINIMUM_DISPLAY_TIME = 300; // Minimum time to display the progress bar is 300 milliseconds
public frmProgressBar(Action worker)
{
InitializeComponent();
if(worker == null)
{
throw new ArgumentNullException();
}
Worker = worker;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Factory.StartNew(Worker).ContinueWith(t => { this.Close(); }, TaskScheduler.FromCurrentSynchronizationContext());
}
protected override void OnClosed(EventArgs e)
{
// the code that you want to measure comes here
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
if (elapsedMs < MINIMUM_DISPLAY_TIME) //ensure progress bar is displayed for at least 100 milliseconds, otherwise it just looks like the parent main form glitches.
{
long lDelay = MINIMUM_DISPLAY_TIME - elapsedMs;
Thread.Sleep((int)lDelay);
}
}
}
}
The form design looks like this : -
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/n4XqU.jpg
And I call it like this: -
using (Yournamespace.frmProgressBar frm = new Yournamespace.frmProgressBar(Yourprocess))
{
frm.ShowDialog(this);
}
Yourprocess is the code that you want to execute that is causing the delay.
The main problem with this implementation is Yourprocess cannot return a value or take parameters. I am sure the code can be changed to accomodate this but I did not have time so I use globals to pass in data and to see results(shame on me).
This is not my code, although I have modified it, came from a you tube video - Wait Form Dialog.
Edit. I forgot to say that I set my form to 100% opacity so the progress bar seems to float above my winform whenever I use it.
回答3:
There are definitely resources to help you figure this out, however I spent like 2 days figuring it out so I'm going to help you out here and save you the headache. The progress bar itself is under the main UI thread (like any objects in your form) and needs to be handled by the main thread. Whatever you are trying to do on the side can be handled by a thread like this
String a, b;
a = txtUsername.Text;
b = txtPassword.Password;
Thread Login = new Thread(() => CompleteLogin(a, b));
InversePbVisibility();
Login.Start();
The InversePbVisibility() method would be replaced by whatever you are doing to make the progress bar visible to the user. A quick side note, any methods that are run on your declared thread can only pass variables and not anything already under the control of the main thread.
来源:https://stackoverflow.com/questions/57377016/how-to-load-the-progressbar-at-the-same-time-my-method-is-running