BackgroundWorker's progress bar is not working - c#

The BackgroundWorker's progressbar is not updated while doing some tasks. What I would like to reach is progressbar moving while iterating through each file in DirectoryInfo. Suppose we have 20 files of ".sql" while first file completed it should be 5%, 10% and etc.
Here is my code.
private void CSV_Click(object sender, RoutedEventArgs e)
{
try
{
btnExtract.IsEnabled = false;
workerextract.RunWorkerAsync();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
DirectoryInfo di = new DirectoryInfo(txtQueryfolder.Text);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
});
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void workerextract_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBarExtract.Value = e.ProgressPercentage;
}
private void workerextract_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnExtract.IsEnabled = true;
System.Windows.MessageBox.Show("CSV Data extraction finished!");
}
I found that
private void workerextract_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
is called once at the end when 100%.
Also,
private void workerextract_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
never called as I do not see Message Box at the end.
So, I think I am doing something wrong here, could you please direct me on right way?

The problem was in wrapping whole DoWork inside Dispatcher.Invoke.
I need to wrap only those code where it is interacting with UI.
So I changed the code appropriately and it works.
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
this.Dispatcher.Invoke(() =>
{
di = new DirectoryInfo(txtQueryfolder.Text);
});
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
this.Dispatcher.Invoke(() =>
{
//System.Windows.MessageBox.Show(line);
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
});
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
Thanks to all for showing the direction.

Using this.Dispatcher.Invoke in the BackgroundWorker's DoWork event you are executing the whole operation in the UI thread; which is what BackgroundWorker born to avoid to do.
Also, you get an error unwrapping your code from the dispatcher because you are accessing an UI object, which is txtQueryfolder.
Just use:
private void workerextract_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string queryFolder = e.Argument.ToString();
try
{
DirectoryInfo di = new DirectoryInfo(queryFolder);
files = di.GetFiles("*.sql").Count();
currentfile = 0;
foreach (FileInfo fi in di.GetFiles("*.sql"))
{
// Open the text file using a stream reader.
using (StreamReader sr = new StreamReader(fi.FullName))
{
// Read the stream to a string, and write the string to the console.
string line = sr.ReadToEnd();
//System.Windows.MessageBox.Show(line);
// ExtractToCSV shouldn't access to a UI object.
ExtractToCSV(line, System.IO.Path.GetFileNameWithoutExtension(fi.Name));
currentfile++;
}
int percentage = (currentfile + 1) * 100 / files;
workerextract.ReportProgress(percentage);
}
}
catch (Exception ex)
{
// Don't use MessageBox in a thread different from the UI one. Just set the result (e.Result) and get that in the RunWorkerCompleted event.
// System.Windows.MessageBox.Show(ex.Message);
}
}
When you call the RunWorkerAsync method just add the parameter like below:
workerextrac.RunWorkerAsync(txtQueryfolder.Text);

Related

Stop thread from working after it fills the queue

To stop The thread based on a flag Finesedequeue In the below thread method I did a loop in the stop button(its purpose is to stop this thread after it fills the queue named _textFromFilesQueue ) and the main problem in this is that my form is freezing
Here is my code:
The Threads Method:
public static bool Finisedqueue ;
public void Read(string inputDirectoryPath)
{
try
{
Thread.CurrentThread.IsBackground = true;
Finisedqueue = false;
Console.WriteLine("thread 1 started");
if (Form1.IsStarted)
{
Thread.Sleep(3000);
var dir = new DirectoryInfo(inputDirectoryPath);
FileInfo[] f = dir.GetFiles("*.txt");
if (f.Length > 0)
{
Console.WriteLine("{0} directory contains {1} Files",inputDirectoryPath,f.Length);
int counter = 0;
foreach (System.IO.FileInfo fi in f)
{
counter++;
var filePath = Path.Combine(fi.DirectoryName, fi.Name);
string textFromFile = File.ReadAllText(filePath);
_textFromFilesQueue.Enqueue(textFromFile);
Console.WriteLine("The text inside file number {0} is : {1}",counter,textFromFile);
fi.Delete();
Console.WriteLine("deleting file number {0} from input folder",counter);
}
Finisedqueue = true;
Console.WriteLine("finished Deleting files");
}
else
{
Console.WriteLine("{0} Has no files inside it ",inputDirectoryPath);
}
}
}
catch (Exception excep)
{
Console.WriteLine("An error occurred: '{0}'", excep);
throw(excep);
}
}
The Stop Button Click:
private void stop_Click(object sender, EventArgs e)
{
while (!Thread1.Finisedqueue)
{
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
Thread1Timer.Dispose();
button3.Enabled = true;
textBox3.Enabled = true;
}
> the main problem in this is that my form is freezing
IF any body can help me in this Thank you very much
When busy waiting it is a good idea to sleep, so that you give other threads a chance to execute.
while (!Thread1.Finisedqueue)
{
Thread.Sleep(100);
Console.WriteLine("Queue from First Thread is still Enqueuing data");
}
You can also check out the Task class
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx which is nice for asynchronous operations such as file IO and is awaitable using the built-in async/await functionality.
private Queue<string> q = new Queue<string>();
private Task readFilesTask;
public void ReadFiles(string inputDirectory)
{
var files = Directory.GetFiles(inputDirectory, "*.txt", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
var textFromFile = File.ReadAllText(file);
q.Enqueue(textFromFile);
File.Delete(file);
}
}
private void start_Click(object sender, EventArgs e)
{
readFilesTask = Task.Run(() => ReadFiles("//path/to/dir"));
}
private void stop_Click(object sender, EventArgs e)
{
readFilesTask.Wait();
//readFilesTask.Wait(1000); // if you want kill the task after waiting for 1000 milliseconds
//Enable buttons
}

Invalid Operation Exception in file saving

I have this code and I don't know why it pops up this error message :
"Invalid Operation Exception was unhandled by user code".
This error comes out when I press the save button.
The purpose of this program is to save the text from one textbox in the Mytest.txt file and then from the file to the textbox1. I would really appreciate some help here.
Thank you in advance.
public MainPage()
{
this.InitializeComponent();
}
private void buttonsave_Click(object sender, RoutedEventArgs e)
{
string path = #"C:\Users\geora\Mytest.txt";
if(!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine(textBox.Text);
}
}
}
private void buttonshow_Click(object sender, RoutedEventArgs e)
{
string path = #"C:\Users\geora\Mytest.txt";
using (StreamReader sr = File.OpenText(path))
{
string s = "";
s = sr.ReadLine();
textBox1.Text = s;
}
}
You have opened files but not closed the file.This Might be the issue
StreamReader sr = File.OpenText(path)
You need to close it. Using does this for you (and disposes it so it's GC'd sooner):
Or alternatively in .Net 2 you can use the new File. static members, then you don't need to close anything:
variable = File.ReadAllText(path);
Additional to your exception
WPF Version:
private void loadButton_Click(object sender, RoutedEventArgs e)
{
try
{
textBox.Text = File.ReadAllText(#"d:\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void saveButton_Click(object sender, RoutedEventArgs e)
{
try
{
File.WriteAllText(#"d:\test.txt", textBox.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
UWP Version:
using System;
using Windows.Storage;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
...
private async void buttonSave_Click(object sender, RoutedEventArgs e)
{
try
{
var storageFolder = ApplicationData.Current.LocalFolder;
var sampleFile = await storageFolder.CreateFileAsync("sample.txt",
CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(sampleFile, textBox.Text);
var msgbox = new MessageDialog(sampleFile.Path, "Your file is in");
await msgbox.ShowAsync();
}
catch (Exception ex)
{
var msgbox = new MessageDialog(ex.ToString(), "Error");
await msgbox.ShowAsync();
}
}
private async void buttonLoad_Click(object sender, RoutedEventArgs e)
{
try
{
var storageFolder = ApplicationData.Current.LocalFolder;
var sampleFile = await storageFolder.GetFileAsync("sample.txt");
textBox.Text = await FileIO.ReadTextAsync(sampleFile);
}
catch (Exception ex)
{
var msgbox = new MessageDialog(ex.ToString(), "Error");
await msgbox.ShowAsync();
}
}

streamReader inside OnChanged fires twice?

I have a small application that watches a specific file and whenever it changes, my application should do the actions in the loop, but something is firing the function more than once!! here's my code
private void OnChanged(object source, FileSystemEventArgs e)
{
if (e.FullPath == #"C:\test.txt")
{
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//actions here
}
}
}
catch (Exception)
{
}
}
}
So I'm guessing in the loop when streamreader do File.OpenText somehow is firing the function again?! any ideas?
From MSDN:
The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, ...
So yes, opening (actually: closing) the file will raise the Changed event again.
You can use the NotifyFilter to limit the actions your watcher triggers on.
SOLUTION
So I did one small thing that controlled the issue, I added a counter and always check if it's not the first time, skip and reassign it to 0.
private int fireCounter = 0;
private void OnChanged(object source, FileSystemEventArgs e)
{
fireCounter++;
if (fireCounter == 1)
{
delete();
if (e.FullPath == #"C:\test.txt")
{
Thread.Sleep(2000);
//I added Sleep for two seconds because without it sometimes it wont work
string textFilePath = #"C:\test.txt";
try
{
using (var streamReader = File.OpenText(textFilePath))
{
var lines = streamReader.ReadToEnd().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
//Actions Here
}
}
}
catch (Exception)
{
}
}
}
else
{
fireCounter = 0;
}
}

Increment ProgressBar within another class

it's my first question I'm asking here, so please be gentle with me ;)
So I've actually got two WinForms in my C# application I'm writing at the moment (I'm quite new to C#).
This window has a button, which saves photos from an usb device you selected before in a list box to another folder.
After clicking on this button my main thread is of course busy with copying, so I decided to create another WinForm which contains my ProgressBar.
Foreach completed copy, I want to increment my ProgressBar accordingly.
So I count the number of copies I have to do and give it the progressbar as maximum. But my problem at the moment is, that I really don't know how to increment the ProgressBar without getting a Thread Unsafe Exception.
Here's my ProgressWindow code:
public partial class ProgressWindow : Form
{
BackgroundWorker updateProgressBarThread = new BackgroundWorker();
private Boolean _isThreadRunning = false;
public Boolean IsThreadRunning
{
get { return _isThreadRunning; }
set { _isThreadRunning = value; }
}
private int _progressbarLength;
public int ProgressbarLength
{
get { return _progressbarLength; }
set { _progressbarLength = value; }
}
private int progress = 1;
public ProgressWindow()
{
Show();
InitializeComponent();
}
private void StartUpdateThread(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Reports progress to the ProgressChangedEvent function. (Thread Safe)
}
private void FinishProgressThread(object sender, RunWorkerCompletedEventArgs e)
{
if (!_isThreadRunning)
{
MessageBox.Show("Erfolgreich kopiert");
Close();
}
}
private void ProgressChangedEvent(object sender, ProgressChangedEventArgs e)
{
this.copyProgressbar.Value = e.ProgressPercentage;
this.progressStatus.Text = e.ProgressPercentage.ToString();
}
public void CallUpdateThread()
{
updateProgressBarThread.WorkerReportsProgress = true;
updateProgressBarThread.DoWork += new DoWorkEventHandler(StartUpdateThread);
updateProgressBarThread.ProgressChanged += new ProgressChangedEventHandler(ProgressChangedEvent);
updateProgressBarThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(FinishProgressThread);
updateProgressBarThread.RunWorkerAsync();
}
}
I want to increment my ProgressBar with 1 after each succesful copy.
How do I do this from my main thread?
This is the function which actually handles the copy process
private void SaveFile(System.IO.DirectoryInfo root)
{
try
{
IEnumerable<DirectoryInfo> directoriesNames = root.EnumerateDirectories();
// New instance of thread ProgressWindow.
ProgressWindow progress = new ProgressWindow();
progress.CallUpdateThread();
foreach (DirectoryInfo element in directoriesNames)
{
// Query all subdirectories and count everything with the in the configuration made settings.
if (!element.Attributes.ToString().Contains("System"))
{
// Now we insert the configuration we applied.
String fileExtension = null;
if (Properties.Settings.Default._configPhoto)
{
fileExtension = "*.jpg";
}
if (Properties.Settings.Default._configWordDocument)
{
fileExtension = "*.odt";
}
FileInfo[] jpgList = element.GetFiles(fileExtension, SearchOption.AllDirectories);
// set the size of the progress bar
progress.ProgressbarLength = jpgList.Count();
// Now we go through all our results and save them to our backup folder.
foreach (FileInfo tmp in jpgList)
{
string sourceFilePath = tmp.FullName;
string destFilePath = PATHTOBACKUP + "\\" + tmp.Name;
progress.IsThreadRunning = true;
try
{
System.IO.File.Copy(sourceFilePath, destFilePath, true);
}
catch (IOException ioe)
{
MessageBox.Show(ioe.Message);
}
}
}
}
// progress.IsThreadRunning = false;
}
catch (UnauthorizedAccessException e)
{
MessageBox.Show(e.Message);
}
}
It's pretty obvious that I have to do this after this function
System.IO.File.Copy(sourceFilePath, destFilePath, true);
But how do I report this to my ProgressWindow?
I really hope I explained it well enough, not sure if I'm missing something important.
Thanks in advance guys
Here is a compact example of the key components:
Clicking button starts new thread worker
Progress is done by file lengths, not by number of files
BeginInvoke used to update the progress bar (avoid cross Thread exception)
ProgressBar pb = new ProgressBar() { Minimum = 0, Maximum = 100 };
Button btn = new Button();
btn.Click += delegate {
Thread t = new Thread(() => {
DirectoryInfo dir = new DirectoryInfo("C:\\temp\\");
var files = dir.GetFiles("*.txt");
long totalLength = files.Sum(f => f.Length);
long length = 0;
foreach (var f in files) {
length += f.Length;
int percent = (int) Math.Round(100.0 * length / totalLength);
pb.BeginInvoke((Action) delegate {
pb.Value = percent;
});
File.Copy(f.FullName, "...");
}
});
t.IsBackground = true;
t.Start();
};

Open a directory and process files/folders in background worker query

I have the following code, I'm trying to open a directory and process the files in it via the Background worker but I am having issues with it.
The error I have is (The name filePath does not exist in the current context), which I can understand because it's stored in another method? if someone could point out to me what is wrong with my code it would be appreciated. Folderbrowser doesn't work under the Background worker section.
private void btnFiles_Click(object sender, EventArgs e)
{
//btnFiles.Enabled = false;
btnSTOP.Enabled = true;
//Clear text fields
listBoxResults.Items.Clear();
listBoxPath.Items.Clear();
txtItemsFound.Text = String.Empty;
//Open folder browser for user to select the folder to scan
DialogResult result = folderBrowserDialog1.ShowDialog();
if (result == DialogResult.OK)
{
//Store selected folder path
string filePath = folderBrowserDialog1.SelectedPath;
}
//Start the async operation here
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Process the folder
try
{
foreach (string dir in Alphaleonis.Win32.Filesystem.Directory.EnumerateFiles(filePath, "*.*", SearchOption.AllDirectories, true))
{
//Populate List Box with all files found
this.Invoke(new Action(() => listUpdate2(dir)));
FileInfo fi = new FileInfo(dir);
if (fi.Length == 0)
{
//Populate List Box with all empty files found
this.Invoke(new Action(() => listUpdate1(dir + Environment.NewLine)));
}
}
}
//Catch exceptions
catch (Exception err)
{
// This code just writes out the message and continues to recurse.
log.Add(err.Message);
//throw;
}
finally
{
//add a count of the empty files here
txtItemsFound.Text = listBoxResults.Items.Count.ToString();
// Write out all the files that could not be processed.
foreach (string s in log)
{
this.Invoke(new Action(() => listUpdate1(s)));
}
log.Clear();
MessageBox.Show("Scanning Complete", "Done", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
//If cancel button was pressed while the execution is in progress
//Change the state from cancellation ---> cancelled
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
//backgroundWorker1.ReportProgress(0);
return;
}
//}
//Report 100% completion on operation completed
backgroundWorker1.ReportProgress(100);
}
#DonBoitnott solution is the most general for data flow inside class. Specifically to BackgroundWorker there is another one exists
private void btnFiles_Click(object sender, EventArgs e)
{
...
// pass folder name
backgroundWorker1.RunWorkerAsync(folderBrowserDialog1.SelectedPath);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// get passed folder name
string filePath = (string)e.Argument;
...
}
The variable "filePath" is being declared local to the btnFiles_Click method. In order for it to be used elsewhere, it must be declared global to the code page:
public class Form1
{
private String _filePath = null;
private void btnFiles_Click(object sender, EventArgs e)
{
//get your file and assign _filePath here...
_filePath = folderBrowserDialog1.SelectedPath;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//use _filePath here...
}
}

Categories

Resources