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();
}
Related
I make an application with WPF. I use a method in another class that goes through all the images in the folder from the MainForm class. I want the ProgressBar to be updated, so I send a request to this class to check for changes in the number of images processed. But it does not even process images. I use the BackgroundWorker class to do this. The MainForm code below:
public partial class MainWindow : Window
{
ImageProcessing detector;
BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
if (PathTextBox.Text != "")
{
detector = new ImageProcessing(isFolder, PathTextBox.Text);
worker.RunWorkerAsync();
}
}
private void MainForm_ContentRendered(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += Worker_CheckChangingProcessedImages;
worker.DoWork += Worker_RunProcessingImages;
worker.ProgressChanged += Worker_ProgressChanged;
}
private void Worker_RunProcessingImages(object sender, DoWorkEventArgs e)
{
detector.DetectTextOnImage();
}
private void Worker_CheckChangingProcessedImages(object sender, DoWorkEventArgs e)
{
while (detector.ReturnNumberProcessedImages() <= Progress.Maximum)
(sender as BackgroundWorker).ReportProgress(detector.ReturnNumberProcessedImages());
}
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int numberProcessed = detector.ReturnNumberProcessedImages();
Progress.Value = numberProcessed;
if (numberProcessed == 0) LogTextBox.Text = "Start processing";
else if ((numberProcessed == 1) && (Progress.Maximum == 1))
LogTextBox.Text += "\n1 image is processed";
else if (numberProcessed % 10 == 0)
LogTextBox.Text += $"\n{numberProcessed} images are processed";
if (numberProcessed == Progress.Maximum)
LogTextBox.Text += "\nEverything is processed";
}
}
The ImageProcessing class has a field with the number of processed images. The DetectTextOnImage() method has a foreach loop. The code below:
private int numberProcessedImages = 0;
public void DetectTextOnImage()
{
foreach (String imageFileName in Directory.GetFiles(path))
{
// do something
numberProcessedImages++;
}
}
private void timer3_Tick(object sender, EventArgs e)
{
}
Here is the full code to do that. Just add a Label named "label1" on your window.
public partial class Form1 : Form
{
private Timer timer3;
private int counter;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
counter = 0;
timer3 = new Timer();
timer3.Interval = 3000;
timer3.Tick += Timer3_Tick;
timer3.Start();
}
private void Timer3_Tick(object sender, EventArgs e)
{
counter++;
label1.Text = counter.ToString();
}
}
I want to load new form in background and show it after loaded.
but I always got this error : "invalid cross-thread access" with the code under:
How to make it work?!
public partial class f1 : Form
{
private Form f2;
public f1()
{
InitializeComponent();
BackgroundWorker bgw = new BackgroundWorker();
bgw.RunWorkerAsync();
bgw.DoWork += new DoWorkEventHandler(dowork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
}
void dowork(object sender, DoWorkEventArgs e)
{
f2 = new f2();
}
void completed(object sender, RunWorkerCompletedEventArgs e)
{
f2.showdialog();
this.Close();
}
}
you may have to separate the data loading for form and form UI initialization into two different faze.
I have changed your code sample to the following.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
BackgroundWorker bgw = new BackgroundWorker();
bgw.RunWorkerAsync();
bgw.DoWork += new DoWorkEventHandler(dowork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
}
void dowork(object sender, DoWorkEventArgs e)
{
e.Result = InitializeNeededDataToShowOnTheSecondform();
}
void completed(object sender, RunWorkerCompletedEventArgs e)
{
BeginInvoke(new MethodInvoker(() =>
{
var f2 = new Form3(e.Result);
f2.ShowDialog();
this.Close();
}));
}
private object InitializeNeededDataToShowOnTheSeconDform()
{
//time consuming data initialization and/or any other time consuming process
return 10;
}
}
This question already has answers here:
Passing Parameter to Backgroundworker
(3 answers)
Closed 7 years ago.
I am wanting to pass an additional parameter to my DoWork method, but am getting a compile error of No overload for 'backgroundWorker1_DoWork' matches delegate 'System.ComponentModel.DoWorkEventHandler'
This is my syntax, what should I do to fix this?
namespace Testing
{
public partial class Form1 : Form1
{
public static string[] employeeName;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void btn1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(employeeName);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e, string[] employeeName)
{
//Just for example sake
for (int q = employeeName.GetLowerBound(0); q <= employeeName.GetUpperBound(0); q++)
{
MessageBox.Show(employeeName[q];
}
}
}
}
DoWorkEventHandler has the following definition :
public delegate void DoWorkEventHandler(
Object sender,
DoWorkEventArgs e
)
You cannot add a third argument. The object you want to get is in DoWorkEventArgs.Argument property.
public partial class Form1 : Form1
{
public static string[] employeeName;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void btn1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync(employeeName);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] employeeName = (string[])e.Argument;
//Just for example sake
for (int q = employeeName.GetLowerBound(0); q <= employeeName.GetUpperBound(0); q++)
{
MessageBox.Show(employeeName[q];
}
}
}
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.