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++;
}
}
Related
I am trying to increment an value in Arduino and send it on port and after that to display it in a label in real-time.
even that I put and delay(200) and Thread.sleep(200);
namespace Receiver
{
public partial class Form1 : Form
{
SerialPort port;
public Form1()
{
InitializeComponent();
this.FormClosed += new FormClosedEventHandler(Form1_FormClosed);
if (port == null)
{
port = new SerialPort("COM9", 9600);//Set your board COM
port.Open();
}
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (port != null && port.IsOpen)
{
port.Close();
}
}
private void Afisare_Click(object sender, EventArgs e)
{
while (true)
{
string a = port.ReadExisting();
afisare.Text = a;
Thread.Sleep(200);
}
}
}
}
in change I got all the values, one by after one,down the screen some of them.
You are running an endless loop in your Afisare_Click handler that runs in the same thread as you UI. This means that the UI will not be able to render the control changes.
Thread.Sleep code switches the context to other threads, but not the UI thread.
Your approach should be using Timer.
public partial class Form1 : Form
{
private Timer _timer;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_timer = new Timer();
_timer.Interval = 200;
_timer.Tick += _timer_Tick;
_timer.Enabled = true;
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
// This function will be called every 200 ms.
// Read the information from port, update the UI.
string a = port.ReadExisting();
afisare.Text = a;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
_timer.Stop();
if (port != null && port.IsOpen)
{
port.Close();
}
}
}
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;
}
}
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();
}
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.
My problem is that I want the form still display data when it increases but the form is blocked and I cannot do anything with it.
This is my code :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
while (true)
for (int i = 1; i < 11; i++)
richTextBox1.Text += "here" + i + "/n";
}
}
}
How I can prevent form from blocking?
private void Form1_Load(object sender, EventArgs e)
{
BackgroundWorker BWorker = new BackgroundWorker();
BWorker.DoWork += BWorker_DoWork;
BWorker.RunWorkerAsync();
}
void BWorker_DoWork(object sender, DoWorkEventArgs e)
{
// while(true) is meaningless.
for (int i = 1; i < 11; i++)
{
Action UpdateUI = () => { richTextBox1.Text += "here" + i + "/n"; };
this.BeginInvoke(UpdateUI);
}
}
Split your working cycle into steps by utilizing timer
private int _workStep;
private void button1_Click(object sender, EventArgs e)
{
_workStep = 0;
timerWork.Start();
}
private void timerWork_Tick(...)
{
switch(workStep)
{
case 0:
... // do something
if(something)
_workStep = 1;
case laststep:
timerWork.Stop();
}
}
or put work into thread (by using Thread, BackgroundWorker or Task), but then you must use Invoke/BeginInvoke when updating something in the user interface (accessing controls).
You can do this to get a responsive ui
delegate void DisplayInvoker(string text);
private void DisplayinRichTextbox(string text)
{
if (this.InvokeRequired)
this.BeginInvoke(new DisplayInvoker(DisplayinRichTextbox), text);
return;
}
richTextBox1.Text += text;
}
private void button1_Click(object sender, EventArgs e)
{
// some synchronization would have to be done kill old
// pool threads when the button is hit again
//
ThreadPool.QueueUserWorkItem((x) =>
{
while (true)
for (int i = 1; i < 11; i++)
DisplayinRichTextbox("here" + i + "/n");
});
}