The calling thread cannot access this object, because another thread owns it - c#

I am using BackgroundWorker for my WPF application, but what is in the Worker_RunDetection function does not work and there is no interface update from Worker_ProgressShow. Worker_ProgressShow is for updating interface and Worker_RunDetection processes images from path I got from user. This application must search for text in images and tell user how many images have already been processed. What`s wrong?
UPD: I used RunWorkerCompleted and got
The calling thread cannot access this object, because another thread owns it.
Not I use Dispatcher.Invoke, but interface freeze. Progress is a component (ProgressBar).
int numberProcessed = 0;
private void MainForm_ContentRendered(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += Worker_RunDetection;
worker.ProgressChanged += Worker_ProgressShow;
}
private void Worker_ProgressShow(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.Invoke(() => {
Progress.Value = Convert.ToInt32(e.UserState);
if (Progress.Value % 10 == 0)
LogTextBox.Text += $"\n{Progress.Value} images are processed";
});
}
private void Worker_RunDetection(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke(() => {
foreach (String imageFileName in Directory.GetFiles(PathTextBox.Text))
{
detector = new ImageProcessing(isFolder, imageFileName, pathFolder);
detector.DetectTextOnImage();
numberProcessed++;
(sender as BackgroundWorker).ReportProgress(numberProcessed);
}
});
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
if (PathTextBox.Text != "")
{
worker.RunWorkerAsync();
}
}

Problem was with the Apth taken from PathTextBox.Text.
It must not reference Controls declared in the UI Thread in the DoWork() handler of a BackGroundWorker.
Everything works fine if the task logic is separated from the UI.
Corrected code:
int numberProcessed = 0;
private void MainForm_ContentRendered(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += Worker_RunDetection;
worker.ProgressChanged += Worker_ProgressShow;
}
private void Worker_ProgressShow(object sender, ProgressChangedEventArgs e)
{
Progress.Value = Convert.ToInt32(numberProcessed);
if (Progress.Value % 10 == 0)
LogTextBox.Text += $"\n{Progress.Value} images are processed";
}
private void Worker_RunDetection(object sender, DoWorkEventArgs e)
{
foreach (String imageFileName in Directory.GetFiles(e.Argument.ToString()))
{
detector = new ImageProcessing(isFolder, imageFileName, pathFolder);
detector.DetectTextOnImage();
numberProcessed++;
(sender as BackgroundWorker).ReportProgress(numberProcessed);
}
}

Related

System.InvalidOperationException CrossThread using worker function

I'm dealing with a thread error using a worker in my function. I already checked about bindings and 'invoke'(?) but I'm quite new to C# and WPF and I don't really understand the way I should solve this. It seems that I'm calling of function from another which is not the "owner".
void myLongLastingFunction(0)
{
line_list.SelectedIndex = 0; //ERROR CrossThread
blablabla..
}
private void btnClick(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoConvertOne;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
private void worker_DoConvertOne(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
//Processing
myLongLastingFunction(0);
//The progress bas is full...
worker.ReportProgress(100, "Done Processing.");
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Converting finished!");
TestProgressBar.Value = 0;
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
TestProgressBar.Value = e.ProgressPercentage;
}
I was inspired by this post.
Error appears when I'm calling myLongLastingFunction() which is crashing my app :
System.InvalidOperationException: 'Calling thread can't reach this object because a other thread is the owner'
Sorry for my translation which is probably faulse but as close to the original message as possible.
Have you got any idea ?
Use the dispatcher:
void myLongLastingFunction(0)
{
Dispatcher.BeginInvoke((Action)(() => {
line_list.SelectedIndex = 0;
}));
}

Why WinForm Gets Stuck with BackgroundWorker?

hi guys i tried to copy some files with this Code everything is good and the app will copy files but in copy progress i cant move my app or do anything
i tried to use thread but its not works i also use backgroundWorker but still nothing the only control that doesnt get stuck is progressBar its works fine here is my code :
public Form1()
{
InitializeComponent();
backgroundWorker1.Dispose();
backgroundWorker1.DoWork += BackgroundWorker_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundWorker1.ProgressChanged += BackgroundWorker_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker2.DoWork += BackgroundWorker2_DoWork;
backgroundWorker2.WorkerReportsProgress = true;
}
private void BackgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
File.Copy(sourcePath, targetPath);
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < fileSize; i++)
{
int p = (i + 1) * 100 / Convert.ToInt32(fileSize);
backgroundWorker1.ReportProgress(p);
}
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbProgress.Text = e.ProgressPercentage.ToString();
progressBar1.Value = e.ProgressPercentage;
}
private void btnTarget_Click(object sender, EventArgs e)
{
folderBrowser = new FolderBrowserDialog();
if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
targetPath += folderBrowser.SelectedPath + #"\" + fileName;
lbTarget.Text = targetPath;
}
}
private void btnSource_Click(object sender, EventArgs e)
{
op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
sourcePath += op.FileName;
lbSource.Text = sourcePath;
fileInfo = new FileInfo(sourcePath);
fileSize = fileInfo.Length / 1024;
fileName = fileInfo.Name;
MessageBox.Show(string.Format("File size is: {0} KB", fileSize));
}
}
private void btnCopy_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
backgroundWorker2.RunWorkerAsync();
}
You're updating the progress bar faster than the UI can update, for every single byte of the file being copied in a tight loop. You're flooding the UI thread with pointless work.
Remove backgroundWorker1, it's not doing anything useful anyway. If you don't have a way to track the progress (which you don't with File.Copy), just use a progress bar without progress (set Style to Marquee).
For testing I created a simple winform application with a button, a label and a background worker and added the following corresponding events:
private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var worker = (BackgroundWorker)sender;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
private void OnBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
labelProgress.Text = e.ProgressPercentage.ToString();
}
private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
labelProgress.Text = "Done";
}
private void OnButtonProgressClick(object sender, System.EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Works as expected.
Could You try to update Your DoWork to this:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int mainProgress = 0;
for (int i = 0; i < fileSize; i++)
{
int calculatedProgress = (i + 1) * 100 / Convert.ToInt32(fileSize);
if(calculatedProgress > mainProgress)
{
mainProgress = calculatedProgress;
backgroundWorker1.ReportProgress(mainProgress);
}
}
}
maybe You are doing so many updates that simply Window Thread is all the time updating only progress and don't have a time to make anything else?

how do I pass info to my main class from a child class

I have two classes, the main from WPF and a child class called 'PersistDataToTable' inside 'PersistDataToTable.Persist' I have a loop and I want to pass that loop incrament back to the main WPF class to show in the progress bar.
I have a backgroundworker thread running the method that hits the loop, I just don't know how to get the data back to the ReportProgress method from the backgroundworker thread.
private void PersistDataToDb(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += delegate(object o, DoWorkEventArgs args)
{
PersistDataToTable persistData = new PersistDataToTable();
persistData.Persist(seriesId);
worker.ReportProgress(loop incrament from persist()??);
};
worker.ProgressChanged += delegate(object o, ProgressChangedEventArgs args)
{
int percentage = args.ProgressPercentage;
progressBar.Value = percentage;
};
worker.RunWorkerAsync();
}
You could pass the worker.ReportProgress as an Action<Int> into your Persist method so it updates back to your UI
Example:
private void PersistDataToDb(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += delegate(object o, DoWorkEventArgs args)
{
PersistDataToTable persistData = new PersistDataToTable();
persistData.Persist(seriesId, worker.ReportProgress);
};
worker.ProgressChanged += delegate(object o, ProgressChangedEventArgs args)
{
int percentage = args.ProgressPercentage;
progressBar.Value = percentage;
};
worker.RunWorkerAsync();
}
public class PersistDataToTable
{
public void Persist(int seriesId, Action<int> progresscallback)
{
// set the progress and call the Action(worker.ReportProgress)
for (int i = 0; i < 100; i++)
{
progresscallback.Invoke(i);
}
}
}
Use the events DoWork and RunWorkerComplete. Here is an example counting to 10 and passing the result to a label:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString(); // getting the result set in DoWork
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int value = 0;
for (int i = 0; i < 10; i++)
value++;
e.Result = value; // setting result for RunWorkerCompleted
}
The msdn article about this: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx

backgroundworker c# [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to use a backgroundworker?
Please point me in to good description of how to use backgroundworker or what I must to use to keep gui from freezing while app does some long process?
Gui freezes when I do pinging process or whatever.. it's just annoying, I would like to display results of pinging different hosts while it pings it, but it's frozen untill it stops pinging all hosts in a list.
Here is an example:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SL_BackgroundWorker_CS
{
public partial class Page : UserControl
{
private BackgroundWorker bw = new BackgroundWorker();
public Page()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
{
bw.CancelAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.tbProgress.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
this.tbProgress.Text = "Done!";
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
}
}
BackgroundWorker worker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();//Calls worker_DoWork on a separate thread.
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)//Runs when worker_DoWork is completed.
{
//
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//
}

BackgroundWorker progressBar - Problem implementing ReportProgress when loop occurs in another class

Hi
I am trying to integrate a background worker with a progress bar but cannot get it right.
I am processing some files and all the processing is done in an external class.
My difficulty is that the looping is inside this class,where usually I do the talking to the backgroundworker.
The good thing is that when processing a file,an event is fired whenever each file has completed processing.
This is my code any suggestions how could this be achieved
BackgroundWorker _bw = new BackgroundWorker
private void RunLongProcess()
{
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = true;
_bw.DoWork += DoWork;
_bw.ProgressChanged += ProgressChanged;
_bw.RunWorkerCompleted += RunWorkerCompleted;
_bw.RunWorkerAsync();//start the process
if (_bw.IsBusy)
_bw.CancelAsync();
}
static void DoWork (object sender, DoWorkEventArgs e)
{
var files=GetFiles();
int fileCount=files.Count;
//usually I do a loop here but all the processing is done inside this class so
var fileProcessor=new FileProcesser();
fileProcessor.ProcessFiles(files);
}
private void OnFileProcessCompleted(object sender, FileEventArgs e)
{
//Event Fired when a file has been processed
//How do I update progressBar.Problem cross threading here.
//What do I do here?????
_bw.ReportProgress(e.FileProcessedCount, e);
}
ProgressChanged (object sender, ProgressChangedEventArgs e)
{
// Update the UI
labelProgress.Text = e.UserState;
progressBar.Value = e.ProgressPercentage;
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
// Console.WriteLine("You canceled!");
else if (e.Error != null)
//Console.WriteLine("Worker exception: " + e.Error.ToString());
else
// Console.WriteLine("Complete: " + e.Result);
}
_bw.ReportProgress(e.FileProcessedCount, e); should not go in OnFileProcessCompleted. This event is fired when DoWork completes. You should place that in DoWork to update the progress bar. So it would look something like this:
private void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = sender as BackgroundWorker;
var files = GetFiles();
int fileCount = files.Count;
var fileProcessor = new FileProcesser();
for(int i = 0; i < fileCount; i++)
{
fileProcessor.ProcessFile(files[i]);
bg.ReportProgress( (uint)((i / (double)fileCount) * 100));
}
}
I would pass in the BackgroundWorker to the FileProcessor like this:
private void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = sender as BackgroundWorker;
var files = GetFiles();
int fileCount = files.Count;
var fileProcessor=new FileProcesser(bg);
fileProcessor.ProcessFiles(files);
}
In the FileProcesser, it would look something like this:
private BackgroundWorker _bg;
public FileProcessor(BackgroundWorker bg)
{
_bg = bg;
}
public void ProcessFiles(Files files)
{
// Process files
// ...
// Report Progress
_bg.ReportProgress(e.FileProcessedCount, e);
}
In that case, one solution is to make a derived class from BackgroundWorker, make it subscribe to the event, and have it send the progress event to the UI thread in the event handler.

Categories