BackgroundWorker Event Never Executes - c#

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
}
}
}

Related

passing parameters to delegate

I want to change the text in a textbox of a Windows Form with different error messages. These error messages are set to an output string using the same method, but i can't pass the string as a parameter.
Here's how i call a new backgroundworker to safely change the text in the textbox:
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync(argument: error));
Then i try to invoke the call:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
string output = e.Argument.ToString();
object[] par = new object[] { output };
Delegate del = new DELEGATE(changeErrortext);
this.Invoke(del,par);
}
private void changeErrorText()
{
textBoxError.Text = output.ToString();
}
I think i've got to assign the output in the object to the one in the changeErrorText, but i don't really know how to do it.
I've tried different methods, but none have worked. I'm new to C#, so tell me if and where i've messed up.
Instantiate the necessary event handlers
Place eventhandlers in the constructor of the form. For example:
public Form1()
{
InitializeComponent(); // Standard initialization component method...
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
Using the DoWork AND the RunWorkerCompleted events
The way you used 'worker.DoWork += worker_DoWork;' should be expanded with the "RunWorkerCompleted" event. The DoWork event for the operation and the RunWorkerCompleted to handle the completed operation. This event is also usefull when you want to use cancellation or exception handling. This also gets called automatically, so there is no need for a dedicated "changeErrorText"-method. Example code:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
}
You don't have to use a Delegate to pass parameters
The parameters can be passed the way you wrote the RunWorkAsync. The only thing you might want to do is placing that method in a (click) event. For example:
private void button1_Click(object sender, EventArgs e)
{
int value = 123; // example input
backgroundWorker1.RunWorkerAsync(argument: value);
}
The full code doc:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
private void button1_Click(object sender, EventArgs e)
{
int value = 123;
backgroundWorker1.RunWorkerAsync(argument: value);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string output = e.Argument.ToString();
e.Result = output;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
}
}
I hope i've helped you with the above explanations.
Sources
Sending Arguments To Background Worker?;
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netcore-3.1

can't get text from clipboard | c#

It doesn't show me anything inside the logBox, it just stays blank
namespace Clipboard_Logger
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
logBox.SelectionStart = logBox.TextLength;
logBox.ScrollToCaret();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (Clipboard.ContainsText(TextDataFormat.Text))
logBox.Text = logBox.Text + Clipboard.GetText(TextDataFormat.Text) + "\r\n";
}
}
}
}
You are using a background thread ( BackGroundWorker.DoWork ) to access controls on the UI thread. Controls can only be accessed from the UI thread.
Try adding a BackGroundWorker.ProgressChanged event and access your control from that. ProgressChanged runs from the UI thread.
Edit from your comment:
No, that's not what I meant, your're creating a new backgroundworker, you should use the existing one, like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ReportProgress(1);
}
Also, you need to copy text to the clipboard.

C# code refactoring Background worker

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);

Show busy loader while executing action

I have modified Background worker private AbortableBackgroundWorker _worker;
public class AbortableBackgroundWorker : BackgroundWorker
{
//Internal Thread
private Thread _workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (_workerThread != null)
{
_workerThread.Abort();
_workerThread = null;
}
}
}
And have method which init BgWorker
private void BusyLoader(Action doWorkAction)
{
if (_worker == null)
{
_worker = new AbortableBackgroundWorker();
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.DoWork += (sender, e) => _worker_DoWork(sender, e, doWorkAction);
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
}
if (!_worker.IsBusy)
_worker.RunWorkerAsync();
}
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingPanel.StopSpin();
_worker.Abort();
_worker.Dispose();
}
private void _worker_DoWork(object sender, DoWorkEventArgs e, Action action)
{
loadingPanel.StartSpin();
this.Dispatcher.Invoke(action);
}
When I call method BusyLoader I want to pass there Action, which should be executed and at this time busy Indicator should be shown.
I have tried It. And it seems to work but only for first call of BusyLoader. Because _worker.DoWork has the same method, as I understand.
How can I manage to change _worker.DoWork method for every new call of BusyLoader ? Or it is bad approach to pass Action like that?
You said it helped so will post comment as an answer
_worker is not null on the second call so _worker_DoWork is not redefined. Try removing and adding.

Background Worker loading screen in Winforms

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.

Categories