I have the code below to copy some folders over to a different location if the user has the checkbox checked for that folder.
I have a backgroundworker and a progresbar. I see that people on this site and even on MSDN gives the same example to update the progressbar with
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
This is all fine and I get the idea on how it works. But what I can't figure out is to implement my checkboxes and copy the folder if it's checked and then update the progress bar depending on how many checkboxes I have. I count the checked boxes and assigned it to prgbarmax.
This is what I have so far:
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i < prgbarmax; i++)
{
int step = (i / prgbarmax) * 100;
if (test1)
{
//do the copy here
backgroundWorker1.ReportProgress(i);
}
if (tes2)
{
//do the copy here
backgroundWorker1.ReportProgress(i);
}
if (test3)
{
//do the copy here
backgroundWorker1.ReportProgress(i);
}
.... so on
}
}
You need to construct a list of folders to be copied, and copy only one folder at each iteration of the for loop. The problem of your code is you tried to copy all the folders in one iteration.
To illustrate the idea in code sample.
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//construct the list of folder to be copied
List<DirectoryInfo> listOfFolders = new List<DirectoryInfo>();
if (test1)
listOfFolders.Add(folder1);
if (test2)
listOfFolders.Add(folder2);
if (test3)
listOfFolders.Add(folder3);
//begin to copy
for (int i = 0; i < listOfFolders.Count; i++)
{
listOfFolders[i].Copy(...); //copy only one folder in the list
int step = ((i + 1) / listOfFolders.Count) * 100; //calculate the progress
backgroundWorker1.ReportProgress(step);
}
}
Related
private void Bgw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
var filesl = GetFiles(#"D:\", "*.*").ToList();
for(int i = 0; i < filesl.Count; i++)
{
FileInfo info = new FileInfo(filesl[i]);
if (File.Exists(info.FullName))
{
dic.Add(filesl[i], info.Length);
}
int progress = (int)(((float)(i + 1) / filesl.Count) * 100);
worker.ReportProgress(progress, filesl[i]);
Thread.Sleep(100);
}
}
it's reporting fine the items in the list in the worker.ReportProgress but the variable int progress value is all the time 0. in this case there are almost 40000 files in filesl.
It's perfectly correct. Due to the amount of files the variable will return "0" (0%) until it reaches at least 400 files, which then the calculation would return "1" (1%). If you do this calculation more precisely (without converting to INT) you will see that the percentage is fractional, 0.005%, 0.0075%...
I'm populating datagridview with random numbers from a given range, but when I generate a big amount of numbers - my program hangs while generating them. That could last more that a minute (depending on the amount). I know that I can show a progress using ProgressBar. I've tried to use it, but I haven't got anythin. Any examples of using it?
Here is my code:
private void button1_Click(object sender, EventArgs e)
{
if (dataGridView1.RowCount > 0) {
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
}
N = int.Parse(textBox1.Text);
range_min = int.Parse(textBox2.Text);
range_max = int.Parse(textBox3.Text);
numbers = new int[N];
if (range_max < range_min) MessageBox.Show("Some alert text");
else if (range_max == range_min) MessageBox.Show("Some alert text");
else
{
dataGridView1.RowCount = N;
for (int i = 0; i < N; i++)
{
numbers[i] = (int)(Math.Round((range_max - range_min) * rndm.NextDouble() + range_min));
dataGridView1[0, i].Value = numbers[i];
}
}
}
Your program "hangs" while adding data to the UI because you are doing all the work in the UI thread effectively blocking the thread until your loop is done. So you need to handle this heavy duty work in a seperate thread. But you can only change the UI from the UI/main thread so something like this would throw an exception:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
for (int i = 0; i < 4000; i++)
{
dataGridView1.Rows.Add(i.ToString());//throws an exception
}
}).Start();
}
The solution for this is to create a method which you can invoke the main thread to execute like so:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
for (int i = 0; i < 10000; i++)
{
this.AddRow(i);
}
}).Start();
}
public void AddRow(int value)
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(() => this.AddRow(value)));
else
dataGridView1.Rows.Add(value.ToString());//do your ui update (add row, update progress bar etc..)
}
In my application i have two Forms (that's my 1st quite big app)
After clicking start button in parent form i want loading panel to appear, and some logic to be done.
Loading panel (it is just another widowss form) contains bunifu loading circle animation (and some text).
Logic part is responsible for collecting names from directory tree, then replacing some text in Ms.Word files on the tree.
When i open loading panel without executing the logic, loading panel is animated properly and everything works fine.
private void bunifuFlatButton1_Click(object sender, EventArgs e)
{
int x = this.Location.X+this.Width/2-75;
int y = this.Location.Y +this.Height/2-175;
Loader_panel LP = new Loader_panel();
LP.Left = x;
LP.Top = y;
LP.Show();
//System.Threading.Thread.Sleep(5000); \\this doesn't help animation to start
if (FormLogic._dataList.Count > 0) \\Here Logic part starts
{
for (int i = 0; i < FormLogic._dataList.Count; i++)
GetDir.GetTarget(FormLogic._dataList[i]);
/*foreach (var directory in FormLogic._dataList)
GetDir.GetTarget(directory);*/
LogList.Items.Add(DateTime.Now + "List isn't empty");// for testing
FormLogic.ClearData();
}
LP.Close();
}
After enabling logic loading panel appears (appearance isn't smooth), but animation doesn't work (it starts to work only when logic part did the work - i tested it by disabling LP.Close(). What can be reason of this problem ?
Additinal question. In .NET environment the code is compiled to work with multiple processor threads or i have to do it manually ?
EDIT 06/08/2018 7:21 CEST
I cant access LogList from GetDir Method (due to processing it by other thread).
I tried multiple Invoke construction, but none of it seemed to work ;/
I am just too rookie to figure it out. I specified more details in code below:
namespace Docr
{
public partial class DocrForm : Form
{
.....
private async void Button1_Click(object sender, EventArgs e)
{
int x = this.Location.X + this.Width / 2 - 75;
int y = this.Location.Y + this.Height / 2 - 175;
Loader_panel LP = new Loader_panel();
LP.Left = x;
LP.Top = y;
LP.Show(); //animation
int count = FormLogic._dataList.Count;
var list = FormLogic._dataList;
await Task.Run(() =>// processing logic during showing animation
{
if (count > 0)
{
for (int i = 0; i < count; i++)
{
GetDir.GetTarget(list[i],LogList); // Passing LogList as and argument
}
Invoke((Action)(() => { LogList.Items.Add(DateTime.Now + "Hi LogList"); }));\\ works fine
}
});
FormLogic.ClearData();
LP.Close();
}
....
}
namespace DocrLogic
{
class GetDir
{
.....
static public void GetTarget(string UserDirectory, ListBox List)// passing ListBox as an Argument
{
var path = UserDirectory;
var TargetDir = new DirectoryInfo(path);
var AllDocs1 = TargetDir.GetFiles("*.doc*", SearchOption.AllDirectories);
var ProperPrefixes = new List<string> { };
Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); })); // THIS DOESN'T WORK
....
}
.....
}
}
You need to make the method async, then use await to wait for the logic to complete. This way the LP form won't be interrupted by the heavy logic.
private async void bunifuFlatButton1_Click(object sender, EventArgs e)
{
int x = this.Location.X+this.Width/2-75;
int y = this.Location.Y +this.Height/2-175;
Loader_panel LP = new Loader_panel();
LP.Left = x;
LP.Top = y;
LP.Show();
int count = FormLogic._dataList.Count;
var list = FormLogic._dataList;
await Task.Run(()=>
{
if(count > 0)
{
for (int i = 0; i < count; i++)
{
GetDir.GetTarget(list[i]);
}
this.Invoke(() => { LogList.Items.Add(DateTime.Now + "List isn't empty"); });
}
});
FormLogic.ClearData();
LP.Close();
}
You have to make your code thread-safe by using Invoke to access not-thread-safe objects such as UI objects, otherwise, it will throw a System.Threading.ThreadAbortException or a System.InvalidOperationException.
Invoke syntax may differ based on your project but you may
see this post to understand the proper ways of using Invoke()
update
You must never try to access a UI object outside invoke. invoke is provided by System.Windows.Forms.Control and depends on the thread which originally created that control. therefore having Invoke on some other random class simply does not work.
In the second part, you need to change
public static void GetTarget(string UserDirectory, ListBox List)// passing ListBox as an Argument
{
...
Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); })); // THIS DOESN'T WORK
}
to
(you need to send the whole invoke line as the action parameter)
public static void GetTarget(string UserDirectory, Action action)// passing the action as an Argument
{
...
action();
}
or
(you need to set Dispatcher to LogList before starting the Task)
public static Control Dispather;
public static void GetTarget(string UserDirectory)// passing the action as an Argument
{
...
Dispather.Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); }));
}
well i am new to C#, and implementing a code, in which i have two buttons, with one acting as starting of data acquisition and storing it in a csv file and other button to stop it.
well codes for all these are as follows:
//button for start DAQ
private void stdaq_Click(object sender, EventArgs e)
{
stopped = false;
process();
}
//button for stoping DAQ
private void spdaq_Click(object sender, EventArgs e)
{
stopped = true;
}
// process function
private process()
{
int iAvail = 0;
int iRead = 0;
string filename = #textBox3.Text;// taking csv file name from user
// jit:
//a function calculating the total number of values and storing it in iAvail
int[] iRawData = new Int32[iAvail];
double[] dScaledData = new Double[iAvail];
//a function transferring the data from buffer and storing it in dscaledData array
List<double> data = new List<double>();
for (int i = 0; i < iAvail; i++)
{
data.Add(dScaledData[i]);
}
Task myFirstTask = Task.Factory.StartNew(()
=>
{
while (stopped == false)
{
Write(data.ToArray(), filename);
// goto jit;
}
});
}
// csv creater and data writer
public static void Write(double[] data, string outputPath)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
if (stopped) break;
sb.AppendLine(string.Join(",", data[i]));
}
if (File.Exists(outputPath))
{
File.AppendAllText(outputPath, sb.ToString());
}
else
{
File.WriteAllText(outputPath, sb.ToString());
}
}
this is what i am implementing, and the problem with this code is that when the data is first transferred and written to the file, then again the same data is written again and again irrespective of new data and i tried implementing that Goto statement(can be seen in comments) but it is giving error - " Control cannot leave the body of an anonymous method or lambda expression ", and if i don't use the While loop the data is not written at all.
So i want to call my process function and to transfer data to csv starting on press of a start button, take fresh data everytime and write it to csv or can say call the process method again from it's start point and to stop it on click of the stop button, but i am unable to do it irrespective of various tries with different loops and some threading functions also.
please help with this.
Assuming you only need to Write once, you should remove this or change it from while to if:
while (stopped == false)
The loop will cause Write to be called infinitely until stopped becomes true.
Also, you might want to change Write to return rather than break if stopped is true, so that you don't write anything if you are supposed to be stopping:
if (stopped) break;
to
if (stopped) return;
If you want to generate data again and really do want to loop forever, just move that code into the loop:
Task myFirstTask = Task.Factory.StartNew(()
=>
{
while (stopped == false)
{
List<double> data = new List<double>();
// TODO: Generate data here - move all relevant code here
Write(data.ToArray(), filename);
}
});
I think this is a job for the BackgroundWorker.
This code will start you up:
public partial class Form1 : Form
{
int loopCounter = 0; // variable just used for illustration
private static BackgroundWorker bw = new BackgroundWorker(); // The worker object
// This function does your task
public void doSomeStuff(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 1000; i++)
{
loopCounter = i; // Pass the loop count to this variable just to report later how far the loop was when the worker got cancelled.
Thread.Sleep(100); // Slow down the loop
// During your loop check if the user wants to cancel
if (bw.CancellationPending)
{
e.Cancel = true;
return; // quit loop
}
}
}
// This button starts your task when pressed
private void button1_Click(object sender, EventArgs e)
{
bw.WorkerSupportsCancellation = true; // Set the worker to support cancellation
bw.DoWork += doSomeStuff; // initialize the event
if (!bw.IsBusy) // Only proceed to start the worker if it is not already running.
{
bw.RunWorkerAsync(); // Start the worker
}
}
// This button stops your task when pressed
private void button2_Click(object sender, EventArgs e)
{
// Request cancellation
bw.CancelAsync();
textBox1.Text = "The bw was cancelled when 'loopCounter' was at: " + loopCounter.ToString();
}
}
One part of my application works loading of Images from the predefined folder. At this time when loading the images it takes more time. Now I figured out that the progress Bar which can let me to tell the progress of loading.
The Problem I faced is:
I can't able to Integrate the BackgroudWorker, Progress Bar with my function.
For Instance the following is the Background worker and Progress bar code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Load file list here
int totalImageCount = 10000;
// Set maximum value of the progress bar
progressBar1.Invoke(new Action(() => { progressBar1.Maximum = totalImageCount; }));
for (int i = 0; i < totalImageCount; i++)
{
// Load a single image here
Thread.Sleep(10);
// User cancelled loading (form shut down)
if (e.Cancel) return;
// Set the progress
progressBar1.Invoke(new Action(() => { progressBar1.Value = i; }));
}
// Cleanup here
}
// Starts the loading
private void button1_Click(object sender, EventArgs e)
{
// Start loading images
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync();
}
// Stops the loading
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Stop the loading when the user closes the form
if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
}
The following is The function which needs to be for Progress Bar
private void LoadImages()
{
string imagesPath = (Application.StartupPath + "/UnknownFaces/");
string[] extensions = new[] { ".jpg", ".jpeg", ".png" };
var allfiles = Directory.GetFiles(imagesPath);
this.imageList1.ImageSize = new Size(256, 250);
this.imageList1.ColorDepth = ColorDepth.Depth32Bit;
foreach (FileInfo fileInfo in filesSorted)
{
try
{
this.imageList1.Images.Add(fileInfo.Name,
Image.FromFile(fileInfo.FullName));
}
catch
{
Console.WriteLine(fileInfo.FullName + " is is not a valid image.");
}
}
this.lstView_un.View = View.LargeIcon;
lstView_un.LargeImageList = this.imageList1;
lstView_un.Items.Clear();
for (int j = 0; j < this.imageList1.Images.Count; j++)
{
ListViewItem item = new ListViewItem();
item.ImageIndex = j;
item.Text = imageList1.Images.Keys[j].ToString();
this.lstView_un.Items.Add(item);
}
}
catch (Exception ex)
{
MessageBox.Show("Something Wrong happen! "+ex.Message);
}
}
I think the main routine works are there:
this.lstView_un.View = View.LargeIcon;
lstView_un.LargeImageList = this.imageList1;
lstView_un.Items.Clear();
for (int j = 0; j < this.imageList1.Images.Count; j++)
{
ListViewItem item = new ListViewItem();
item.ImageIndex = j;
item.Text = imageList1.Images.Keys[j].ToString();
this.lstView_un.Items.Add(item);
}
The slow part of your code is actually the loop that reads the files, not the loop that populates your ListView.
The best way to report or otherwise present progress state in the UI from BackgroundWorker is to use the ProgressChanged event. However, the code example you are working from will work as well. That is, just update the ProgressBar object directly from your worker code. It fails to take full advantage of the features that BackgroundWorker provides (and indeed, raises the question of why bother with BackgroundWorker if you're not going to use its features), but it will work.
For example:
var allfiles = Directory.GetFiles(imagesPath);
this.imageList1.ImageSize = new Size(256, 250);
this.imageList1.ColorDepth = ColorDepth.Depth32Bit;
// Set the maximum value based on the number of files you get
progressBar1.Invoke((MethodInvoker)(() => { progressBar1.Maximum = filesSorted.Count(); }));
foreach (FileInfo fileInfo in filesSorted)
{
try
{
this.imageList1.Images.Add(fileInfo.Name,
Image.FromFile(fileInfo.FullName));
}
catch
{
Console.WriteLine(fileInfo.FullName + " is is not a valid image.");
}
// Update the ProgressBar, incrementing by 1 for each iteration of the loop
progressBar1.Invoke((MethodInvoker)(() => progressBar1.Increment(1)));
}
Note: your code example is incomplete, and doesn't make sense even as a snippet. One problem in particular is that you retrieve the file names into the array allfiles, but you are iterating on a completely different collection, filesSorted. I did my best with the code you provided to work with, but since the code you posted wouldn't work as-is, you may well have to make some minor adjustments to the example I've provided to get it to do what you want.
If you are unable to figure this out with the above, please provide a good, minimal, complete code example that reliably illustrates your scenario and what precisely you're having trouble figuring out.