I have a simple Backgroundworker and want to write my result to the Console and I also want to report the process.
class Program
{
private static BackgroundWorker worker;
static int counter;
static void Main(string[] args)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
Console.ReadLine();
}
static void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
counter++;
worker.ReportProgress(counter);
}
}
static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine(counter);
}
static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
}
But how can I give my String (or more then 1 String) to my ReportProcess Function?
You're not assigning the event handlers to the BackgroundWorker events:
static void Main(string[] args)
{
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork; //here
worker.ProgressChanged += Worker_ProgressChanged; //and here
worker.WorkerReportsProgress = true;
worker.RunWorkerAsync();
Console.ReadLine();
}
Cheers
You're passing counter as the first (int percentProgress) parameter to ReportProgress, which you can access as shown in the example below using e.ProgressPercentage. You can also pass a second (object userState) parameter, which could be a string or any other object, accessed as shown in the example below using e.UserState.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker.ReportProgress(100, "Complete!");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
toolStripProgressBar.Value = e.ProgressPercentage;
toolStripStatusLabel.Text = e.UserState as String;
}
See full example at https://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripprogressbar(v=vs.110).aspx
Related
On .NET Windows form, I have Background worker component that works fine. I have 5 forms, that has basically same Background worker on it with same code.
Can I extract this code to other class and somehow use it, considering this is an event? This is code I have on form. It takes 20 lines of code, and it would be nice if this can be refactored. Note: as you can see, I have already put it to other class BackgroundWorkerHelper, but can I also somehow refactor this events on Background worker, so that it is in other class as well, this way code is less and reused.
private void RunBackgroundWorker(string infoLabelText, int imageIndex)
{
BackgroundWorkerHelper.Run(backgroundWorker, progressBar, infoLabelText, imageIndex);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorkerHelper.DoWork(backgroundWorker);
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorkerHelper.ProgressChanged(sender, e, progressBar);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorkerHelper.RunWorkerCompleted(sender, e, progressBar);
}
Note: for now I would like to avoid using user control. I know I could do it, but then you have code that handles placing user control and so on. I am still not very good in it.
Here is solution, thanks to rory who gave me idea how to do it. First, I made this class:
public class BackgroundWorkerHelper
{
private static string _infoLabelText = string.Empty;
public BackgroundWorker _BackgroundWorker;
private BarEditItem _marqueeInfo;//this is marquee progress bar
public BackgroundWorkerHelper(BarEditItem marqueeInfo)
{
_marqueeInfo = marqueeInfo;
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.WorkerSupportsCancellation = true;
_BackgroundWorker.DoWork += backgroundWorker_DoWork;
_BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
_BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}
public void Run(string labelText, int imageIndex)
{
_marqueeInfo.Caption = labelText;
_marqueeInfo.ImageIndex = imageIndex;
if (!_BackgroundWorker.IsBusy)
_BackgroundWorker.RunWorkerAsync();
else
_marqueeInfo.Caption = "Busy processing saving data, please wait...";
}
public void DoWork()
{
for (int i = 0; i <= 5; i++)
{
_BackgroundWorker.ReportProgress(i); // call backgroundWorker_ProgressChanged event and pass i (which is e argument e.ProgressPercentage) to update UI controls
Thread.Sleep(250);
}
}
public void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Always;
}
public void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_marqueeInfo.Visibility = BarItemVisibility.Never;
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DoWork();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressChanged(sender, e);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RunWorkerCompleted(sender, e);
}
then in FORM, in class level above constructor place
private readonly BackgroundWorkerHelper _backgroundWorkerHelper;
then in Form Constructor instantiate class
_backgroundWorkerHelper = new BackgroundWorkerHelper(marqueeInfo);
and then I just call it in my form
_backgroundWorkerHelper.Run("Saving", 14);
I am attempting to use a Backgroundworker to keep my Main UI thread open and not freezing up. I am stepping thro my code and have set a breakpoint on both the backgroundWorker1.RunWorkerAsync(); which once hits just leaves the method and on the foreach line -> which is never hit.
What is the proper way to use a Backgroundworker?
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void btnQuery_Click(object sender, EventArgs e)
{
grid1.Rows.Clear();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string name in studentRoster)
{
InsertIntoDB();
}
}
Here is your code with the handlers added and some comments.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
}
private void btnQuery_Click(object sender, EventArgs e)
{
grid1.Rows.Clear();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string name in studentRoster)
{
InsertIntoDB();
// You can report progress by calling the following function.
//backgroundWorker1.ReportProgress(int percentProgress, object userState)
// You can set the percentProgress to any valid integer value,
// and userState can be any object you want.
// You can also check to see if this operation has been sent a request to cancel.
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
// You can send information back to the main thread by setting e.Result to any object you want.
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Do something with the event that is being raised.
// To pass a value back through to this event, use the percentProgress and userState
// parameters of the ReportProgress function.
// the userState object that you pass will be received here as e.UserState
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This event is raised by the background worker when the DoWork method is completed.
// You can receive information back from the worker thread by evaluating e.Result
}
}
}
I am currently creating a Windows Form Application and I am wanting to use a BackgroundWorker. I have created a very simple example which works perfectly:
public partial class Form1 : Form
{
private BackgroundWorker bgw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bgw.WorkerReportsProgress = true;
bgw.DoWork += new DoWorkEventHandler(DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Completed);
}
private void button1_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
bgw.ReportProgress(i * 10, i.ToString());
Thread.Sleep(1000);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("{0}% : Message = '{1}'", e.ProgressPercentage, e.UserState.ToString());
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed");
}
}
Now, when I move the same code to my current application it does not fire. The only difference is that instead of running the code at the Form level, I am attempting to run it inside a custom User Control. As such:
public partial class LobbyForm : UserControl
{
private BackgroundWorker bgw = new BackgroundWorker();
public LobbyForm()
{
InitializeComponent();
bgw.WorkerReportsProgress = true;
bgw.DoWork += new DoWorkEventHandler(DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Completed);
}
public LobbyForm(List<TaskFile> tasks)
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
bgw.ReportProgress(i * 10, i.ToString());
Thread.Sleep(1000);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label5.Text = string.Format("{0}% : Message = '{1}'", e.ProgressPercentage, e.UserState.ToString());
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed");
}
}
Any thoughts on if I am missing something? Perhaps something I am misunderstanding with attempting to run this from a User Control?
I just copied your code and tested it and it worked perfectly if you drag-drop the user control using the designer.
However, if you create he control at runtime and add it to your form, make sure you're using the correct constructor.
LobbyForm lf = new LobbyForm();
this runs this constructor:
public LobbyForm()
{
InitializeComponent();
bgw.WorkerReportsProgress = true;
bgw.DoWork += new DoWorkEventHandler(DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Completed);
}
and not
LobbyForm lf = new LobbyForm(tasks);
which runs this constructor (that doesn't hook up events):
public LobbyForm(List<string> tasks)
{
InitializeComponent();
}
Solution (Call the default constructor from the second one)
public LobbyForm(List<string> tasks) : this()
{
//InitializeComponent();
}
static BackgroundWorker worker;
static void Main(string[] args)
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
Console.ReadLine();
}
static void worker_DoWork(object sender,DoWorkEventArgs e)
{
string strClientId = "2211"; Authenticate(ref strClientId);
}
static void Authenticate(ref string strClientId)
{
Timer timer = new Timer(500);
//Error in this line
//Cannot use ref or out parameter 'strClientId' inside an anonymous method,lambda expression or query expression
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e,ref strClientId);
//timer.Elapsed += Authenticates_Timer();
timer.Start();
}
static void Authenticates_Timer(object sender, ElapsedEventArgs e, ref string strClientId)
{
//want to use variable here
strClientId = "";
}
if there is a way i can use ref variable while passing
parameter to event Cannot use ref or out parameter 'strClientId'
inside an anonymous method,lambda expression or query
expression.I know this is repeated question but i am unable to
figure out how to solve this have refered different posts and
posted question in codeproject too but no one is responding any
help is appreciated
I would recommend sharing strClientId as static variable:
static BackgroundWorker worker;
static string strClientId;
static void Main(string[] args)
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
Console.ReadLine();
}
static void worker_DoWork(object sender,DoWorkEventArgs e)
{
strClientId = "2211";
Authenticate();
}
static void Authenticate()
{
Timer timer = new Timer(500);
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e);
timer.Start();
}
static void Authenticates_Timer(object sender, ElapsedEventArgs e)
{
strClientId = "";
}
Will it work for you?
Update: Ok, let's try this one:
static void worker_DoWork(object sender,DoWorkEventArgs e)
{
string strClientId = "";
var setClientId = new Action<string>(v => { strClientId = v; });
setClientId("2211");
Authenticate(setClientId);
}
static void Authenticate(Action<string> setClientId)
{
Timer timer = new Timer(500);
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e, setClientId);
timer.Start();
}
static void Authenticates_Timer(object sender, ElapsedEventArgs e, Action<string> setClientId)
{
setClientId("");
}
As simple as that:
static void Authenticate(ref string strClientId)
{
Timer timer = new Timer(500);
var strClientIdVar = strClientId;
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e,ref strClientIdVar);
timer.Start();
}
I have a Form where one can Sign In and it could take a while till the data gets loaded into the Form. So I wanted to create a seperate Form (loadScreen.cs) with a Progress Bar when the Form is loading. I tried this in the loadScreen.cs Form:
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
System.Threading.Thread.Sleep(50);
}
e.Result = percentFinished;
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}
I've read that the worker_DoWork method should have the code which takes longer to load. I don't know how to handle this since my button is in Form1. When it's clicked then I go to another class with
private void signIn_Click(object sender, EventArgs e)
{
var logIn = new LogIn(this);
logIn.checkUserInput(this);
}
and there I execute the operations which load certain things. How to connect everything? I need help!
I'm actually in the process of creating a general-purpose dialogue for this sort of thing. It's not going to be ready in time to be of use to you but I would suggest that you go along similar lines. Create your "Loading" dialogue so that it accepts a delegate and invokes it in the DoWork event handler. The main form can then contain a method that does the work and you can pass a delegate for that method to the dialogue. I'll post a very basic example.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private DataTable table;
private void button1_Click(object sender, EventArgs e)
{
var work = new Action(GetData);
using (var f2 = new Form2(work))
{
f2.ShowDialog();
this.dataGridView1.DataSource = this.table;
}
}
private void GetData()
{
this.table = new DataTable();
using (var adapter = new SqlDataAdapter("SELECT * FROM MyTable", "connectionstring here"))
{
adapter.Fill(table);
}
}
}
public partial class Form2 : Form
{
private Action work;
public Form2(Action work)
{
InitializeComponent();
this.work = work;
}
private void Form2_Load(object sender, EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.work();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.DialogResult = DialogResult.OK;
}
}
Note that there's no real way to measure progress when using a data adapter so you could only really display a marquee progress bar in this case.