I made a simple WF in C# in an attempt to change the label dynamically.
However, when I run this code, there is no visible change, until after the code has run, and then it changes to "Processing 9" 0-8 are not ever shown. Is it because it is within the loop?
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
label9.Text = "Processing " + i.ToString();
Thread.Sleep(1000);
}
}
EDIT: X-Tech's code worked but when I tried to incorporate it into my code, I get the following threading issue. I am trying to dynamically change the label.text in the loop while the progress bar is rocking:
An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on.
If there is a handler for this exception, the program may be safely continued
I am not sure how to attack this since I am not super familiar with Task.Factory.StartNew() and Invoke statements.
EDIT 2: Adapted Code:
Task.Factory.StartNew(() =>
{
for (int i = 0; i < lstFilesToZip.Items.Count; i++)
{
zipArray[i] = lstFilesToZip.Items[i].ToString();
// Report progress.
ExecuteSecure(() => label9.Text = "Processing :" + Path.GetFileNameWithoutExtension(lstFilesToZip.Items[i].ToString()));
ExecuteSecure(() => progressBar1.PerformStep());
string zipFileName = Path.GetFileName(zipArray[i]);
string newFolder = txtDestinationFolder.Text + "\\" + Path.GetFileNameWithoutExtension(zipArray[i]);
//check to see if a directory with the file name for the zip file already exists, if not, it will create it
if (Directory.Exists(newFolder) == false)
{
DirectoryInfo newPath = Directory.CreateDirectory(newFolder);
if (lstCommonFiles.Items.Count > 0)
{
//copies each file in the common file list to each new folder created, the boolean indicates that it will overwrite existing files (true) or won't (false)
for (int k = 0; k < lstCommonFiles.Items.Count; k++)
{
File.Copy(lstCommonFiles.Items[k].ToString(), (newFolder + "\\" + Path.GetFileName(commonArray[k])), true);
}
}
//adds the zip file into the folder as well
File.Copy(zipArray[i], (newFolder + "\\" + zipFileName), true);
}
}
if (txtCommonFiles.Text.Length <= 0)
{
string result = "There are no common files selected, would you still like to proceed?";
DialogResult result1 = MessageBox.Show(result, "Common Files Missing", MessageBoxButtons.YesNo);
if (result1 == DialogResult.No)
{
return;
}
}
string[] dirs = Directory.GetDirectories(txtDestinationFolder.Text);
// Grabs the folders in the newly created directory
foreach (string dir in dirs)
lstDestinationFinal.Items.Add(dir);
//send sample file contents to preview window
string[] sampleFiles = Directory.GetFiles(lstDestinationFinal.Items[0].ToString());
//grabs the files
foreach (string zipFiles in sampleFiles)
lstSampleFile.Items.Add(zipFiles);
ExecuteSecure(() =>tabControl1.SelectedTab = tabPage2);
});
}
And the added Method:
private void ExecuteSecure(Action action)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => action()));
}
else
{
action();
}
}
Try this
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
Invoke(new MethodInvoker(() => label9.Text = "Processing " + i.ToString()));
Thread.Sleep(1000);
}
});
EDIT:
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
// Any GUI control which you want to use within thread,
// you need Invoke using GUI thread. I have declare a method below for this
//Now use this method as
ExecuteSecure(() => label9.Text = "Processing " + i);
ExecuteSecure(() => progressBar1.Value = i * 10);
//... other code etc.
Thread.Sleep(1000);
}
});
//---
private void ExecuteSecure(Action action)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => action()));
}
else
{
action();
}
}
Use a BackgroundWorker if you want your UI to be updated in real time, else all the work is done on the UI thread and it remains frozen until the work is over.
private void button1_Click(object sender, EventArgs e)
{
int limit = 10;
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += (s, ee) =>
{
for (int i = 0; i < limit; i++)
{
bw.ReportProgress(i);
Thread.Sleep(1000);
}
};
bw.ProgressChanged += (s, ee) => label9.Text = "Processing" + ee.ProgressPercentage.ToString();
bw.RunWorkerAsync();
}
Maybe try
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
label9.Text = "Processing " + i.ToString();
Thread.Sleep(1000);
Form1.Refresh();
}
}
Try this:
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
label9.Text = "Processing " + i.ToString();
Application.DoEvents();
Thread.Sleep(1000);
}
}
Related
This question already has answers here:
Why does integer division in C# return an integer and not a float?
(8 answers)
Closed 5 years ago.
My program is supposed to allow the user to select multiple text files, compile the contents into a single textfile and then export it into an Excel file. I have tried using the progress bar but it only gets updated after the main loop is finished and not during it. The progress bar will jump from the min value to the max value in an instant once the loop is done. It's not slowly increasing like it suppose to. Here is my code:
public partial class Form1 : Form
{
List<string> path = new List<string>();
Workbook excelworkbook = new Workbook();
Worksheet excelworksheet = new Worksheet("First sheet");
public Form1()
{
InitializeComponent();
}
private void addfiles_Click(object sender, EventArgs e)
{
DialogResult dr = this.selectFiles.ShowDialog();
path.Clear();
if (dr == System.Windows.Forms.DialogResult.OK)
{
// Read the files
foreach (String file in selectFiles.FileNames)
{
try
{
path.Add(file);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
numFiles.Text = path.Count.ToString();
}
}
private void export_Click(object sender, EventArgs e)
{
export.Text = "Exporting...";
export.Enabled = false;
addfiles.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
backgroundWorker1.ReportProgress(15); //15% on progress bar
string[] readText = File.ReadAllLines(path[0]);
string[] readText_secondary;
string filename;
int real_length = readText.Length - 3; //create a text file that stores contents temporarily from multiple textfiles first..later will export into an excel file
using (StreamWriter streamWriter = new StreamWriter(string.Concat(Directory.GetCurrentDirectory(), "\\Temp_textfile.txt"), false)) //create temp text file
{
for (int i = 12; i <= real_length; i++)
{
backgroundWorker1.ReportProgress(((i / real_length) * 75) + 15); //suppose to update the UI here
streamWriter.Write(string.Concat(readText[i].Substring(4, 42))); //initial columns
streamWriter.Write(string.Concat(readText[i].Substring(59, 13))); //upperlimit column
if (i != 12)
{
streamWriter.Write(string.Concat(readText[i].Substring(46, 13))); //results column
}
else if (i == 12)
{
filename = Path.GetFileNameWithoutExtension(path[0]); //serial number column
streamWriter.Write(string.Concat(filename + " "));
}
for (int x = 1; x < path.Count; x++)
{
readText_secondary = File.ReadAllLines(path[x]); //secondary files
if (x != (path.Count - 1))
{
if (i != 12)
{
streamWriter.Write(string.Concat(readText_secondary[i].Substring(46, 13))); //write results
}
else if (i == 12)
{
filename = Path.GetFileNameWithoutExtension(path[x]); //write serial number header
streamWriter.Write(string.Concat(filename + " "));
}
}
else
{
if (i != 12)
{
streamWriter.WriteLine(string.Concat(readText_secondary[i].Substring(46, 13))); //write results for last column
}
else if (i == 12)
{
filename = Path.GetFileNameWithoutExtension(path[x]); //write serial number header for last column
streamWriter.WriteLine(string.Concat(filename + " "));
}
}
}
}
}
var lines = File.ReadAllLines(string.Concat(Directory.GetCurrentDirectory(), "\\Temp_textfile.txt"));
var rowcounter = 0;
foreach (var line in lines)
{
var columncounter = 0;
backgroundWorker1.ReportProgress(90);
string[] values = new string[200]; //extract the initial columns
values[0] = line.Substring(0, 10); values[1] = line.Substring(11, 8); values[2] = line.Substring(20, 8); values[3] = line.Substring(29, 12); values[4] = line.Substring(42, 12);
for (int y = 0; y < path.Count; y++)
{
values[5 + y] = line.Substring((55 + (13 * y)), 13); //extract results column
}
foreach (var value in values)
{
excelworksheet.Cells[rowcounter, columncounter] = new Cell(value); //write column by column
columncounter++;
}
rowcounter++;
}
excelworkbook.Worksheets.Add(excelworksheet); //create the excel file from object
string timedate = DateTime.Now.ToString("dd-MM-yyyy");
string excel_name = "\\ExportedData_" + timedate;
int counter = 1;
string new_path = string.Concat(Directory.GetCurrentDirectory() + excel_name + ".xls"); //this part is to prevent overwriting
while (File.Exists(new_path))
{
new_path = string.Concat(Directory.GetCurrentDirectory() + excel_name + "(" + counter + ")" + ".xls");
counter++;
}
excelworkbook.Save(new_path); //save into path
if (File.Exists(string.Concat(Directory.GetCurrentDirectory(), "\\Temp_textfile.txt"))) //delete temporary text file
{
File.Delete(string.Concat(Directory.GetCurrentDirectory(), "\\Temp_textfile.txt"));
}
backgroundWorker1.ReportProgress(100);
}
catch (Exception ex)
{
MessageBox.Show("Error occurred!\n" + ex, "Error Window", MessageBoxButtons.OK, MessageBoxIcon.Warning);
addfiles.Text = "Add Files";
export.Text = "Export!";
numFiles.Clear();
addfiles.Enabled = true;
export.Enabled = true;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done exporting!", "Notification Window", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
addfiles.Text = "Add Files";
export.Text = "Export!";
numFiles.Clear();
addfiles.Enabled = true;
export.Enabled = true;
}
}
I cant seem to what's the problem here. Can someone point me to the right direction?
backgroundWorker1.ReportProgress(((i / real_length) * 75) + 15);
The issue is that (i / real_length) returns 0 since you are using integer division.
Instead you need to do:
backgroundWorker1.ReportProgress((int)((((double)i / real_length) * 75) + 15));
or:
backgroundWorker1.ReportProgress(i * 75 / real_length + 15);
I would recommend the former.
I have an application which must contain the items in large quantities and continuously into listviews control:
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
deg_loadfile load = new deg_loadfile(loadfile);
load.BeginInvoke(new AsyncCallback(loadcallback),null);
}
void countfile() {
int listcount = listView1.Items.Count;
for (int x = 0; x < listcount; x++) {
listView1.BeginInvoke((MethodInvoker)delegate {
listView1.Items[x].SubItems[1].Text = "ok";
listView1.Items[x].SubItems[2].Text = "ok";
listView1.Items[x].SubItems[3].Text = "done";
},x);
}
}
void countcallback(IAsyncResult ae) {
MessageBox.Show("count finished");
}
delegate void deg_count();
void loadcallback(IAsyncResult ae) {
MessageBox.Show("finished");
}
delegate void deg_loadfile();
void loadfile() {
string file = File.ReadAllText("hughlist.txt");
string[] files = Regex.Split(file,"\n");
foreach (string str in files) {
listView1.BeginInvoke((MethodInvoker)delegate {
ListViewItem item = new ListViewItem(str);
item.SubItems.Add("");
item.SubItems.Add("");
item.SubItems.Add("");
listView1.Items.Add(item);
},str);
}
}
private void button2_Click(object sender, EventArgs e)
{
deg_count count = new deg_count(countfile);
count.BeginInvoke(new AsyncCallback(countcallback),null);
}
The code above throws system.argumentoutofrangeexception (please see image below)
link image: http://i.stack.imgur.com/WJ7sA.png
The iteration has exceeded the conditions that I have given, why did it happen (note:in my case, i must use "for" instead of "foreach" or other enumeration) ?
thx
UPDATE:
thx to #saruman for the answer (keyword is "access to modified closure"), i've updated the code as below
for (int x = 0; x < listcount; x++) {
var x1=x;
listView1.BeginInvoke((MethodInvoker)delegate
{
listView1.Items[x1].SubItems[1].Text = "ok";
listView1.Items[x1].SubItems[2].Text = "ok";
listView1.Items[x1].SubItems[3].Text = "done";
}, x1); }
Look up access to modified closure
Try this
var listcount = listView1.Items.Count;
for (var x = 0; x < listcount; x++)
{
var x1 = x;
listView1.BeginInvoke((MethodInvoker)delegate
{
listView1.Items[x1].SubItems[1].Text = "ok";
listView1.Items[x1].SubItems[2].Text = "ok";
listView1.Items[x1].SubItems[3].Text = "done";
});
}
How can I do a counter in my Windows Form for count row many rows was read ?
When I execute my Windows Form this counter are shown just when the application processed every data.
Sample Code
public Form1()
{
InitializeComponent();
setMessage(string.Empty, this.lblErro);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i <= xmlnode.Count - 1; i++)
{
int cont = i;
ThreadPool.QueueUserWorkItem(_ =>
{
setMessage(++cont + " of " + xmlnode.Count, this.lblCounter);
});
}
void setMessage(string message, Label lbl)
{
if (InvokeRequired)
{
Invoke((MethodInvoker)(() => setMessage(message, lbl)));
}
else
{
lbl.Text = message;
}
}
}
I tryied the code above but without success. Still shown the message when the app processed all the data
You need to run this loop:
for (int i = 0; i <= xmlnode.Count - 1; i++)
{
...
}
off of the main UI thread. There are a number of ways to accomplish this, but I prefer the BackgroundWorker for whatever reason. You could do it like this:
BackgroundWorker _worker = new BackgroundWorker();
and then in the ctor:
_worker.DoWork += (s, e) =>
{
// place the entire for loop in here
}
and now when you're ready to run it then just do this:
_worker.RunWorkerAsync();
Try putting the loop inside:
ThreadPool.QueueUserWorkItem(_ => {
for (int i = 0; i <= xmlnode.Count - 1; i++)
{
int cont = i;
setMessage(++cont + " of " + xmlnode.Count, this.lblCounter);
}
});
I've got an application that reads all the files and sub folders within the built assembly directory and display it using a datagridview. But I when try to run the application in my network drive to try scanning the files within that drive, it gives out the exception "Access to path 'F:/System File Volume' is denied" and then the application will stop running. Any idea on how to get pass the System File Volume, and still display those files which can be access. Here is my code if needed :
private void Form1_Load(object sender, EventArgs e)
{
count = 0;
timer = new Timer();
timer.Interval = 1000;
timer.Tick += new EventHandler(timer1_Tick);
timer.Start();
//FileIOPermission permit;
try
{
s1 = Directory.GetFiles(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "*.*", SearchOption.AllDirectories);
//permit = new FileIOPermission(FileIOPermissionAccess.AllAccess, s1);
//permit.AddPathList(FileIOPermissionAccess.AllAccess, s1);
for (int i = 0; i <= s1.Length - 1; i++)
{
if (i == 0)
{
dt.Columns.Add("File_Name");
dt.Columns.Add("File_Type");
dt.Columns.Add("File_Size");
dt.Columns.Add("Create_Date");
}
FileInfo info = new FileInfo(s1[i]);
FileSystemInfo sysInfo = new FileInfo(s1[i]);
dr = dt.NewRow();
dr["File_Name"] = sysInfo.Name;
dr["File_Type"] = sysInfo.Extension;
dr["File_Size"] = (info.Length / 1024).ToString();
dr["Create_Date"] = sysInfo.CreationTime.Date.ToString("dd/MM/yyyy");
dt.Rows.Add(dr);
if ((info.Length / 1024) > 1500000)
{
MessageBox.Show("" + sysInfo.Name + " had reach its size limit.");
}
}
if (dt.Rows.Count > 0)
{
dataGridView1.DataSource = dt;
}
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show("Error : " + ex.Message);
throw;
}
}
private bool IsIgnorable(string dir)
{
if (dir.EndsWith(":System Volume Information")) return true;
if (dir.Contains(":$RECYCLE.BIN")) return true;
return false;
}
private void timer1_Tick(object sender, EventArgs e)
{
count++;
if (count == 60)
{
count = 0;
timer.Stop();
Application.Restart();
}
}
public string secondsToTime(int seconds)
{
int minutes = 0;
int hours = 0;
while (seconds >= 60)
{
minutes += 1;
seconds -= 60;
}
while (minutes >= 60)
{
hours += 1;
minutes -= 60;
}
string strHours = hours.ToString();
string strMinutes = minutes.ToString();
string strSeconds = seconds.ToString();
if (strHours.Length < 2)
strHours = "0" + strHours;
if (strMinutes.Length < 2)
strMinutes = "0" + strMinutes;
if (strSeconds.Length < 2)
strSeconds = "0" + strSeconds;
return strHours + ":" + strMinutes + ":" + strSeconds;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
BindingSource bind = new BindingSource();
bind.DataSource = dt;
bind.Filter = string.Format("File_Name like '%{0}%'", textBox1.Text.Trim());
}
You will have to create a try/catch block inside of your for loop and deal with the error for each file on its own, then you can continue the loop afterwards.
I would suggest making a function out of it, Here is some "air code", untested, probably syntactic wrong, but I think you get the idea:
bool GetFileInformation(File f, out string name)
{
name=null;
try
{
FileInfo info = new FileInfo(f);
FileSystemInfo sysInfo = new FileSystemInfo(f);
name=sysInfo.Name;
}
catch(Exception ex)
{
return false;
}
return true;
}
Now you can easily build the loop around this:
for (int i = 0; i <= s1.Length - 1; i++)
{
if (GetFileInformation(s1[i], out name)
{
dr = dt.NewRow();
dr["File_Name"] = name;
// ....
}
}
can i make this loop continue in such a way that after last item in listbox to go to the first one and so on...
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
{
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
string queryhere = listBox1.Items[i].ToString();
this.SetTextappend("" + queryhere + "\n");
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 1);
}
}
}
}
Any help would be greatly appreciated!
Thank you for all the answers
it seems that my list was going backwads so i must replace
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
with
for (int i=0;i<ListBox1.Items.Count;i++)
You are counting down so I think it is "after the first one, go to the last (again)".
Your for-loop can become:
int i = 0;
for(;;) // or while(true)
{
if (i <= 0)
i = listBox1.Items.Count;
i -= 1;
if (worker.CancellationPending)
{
...
}
}
But I notice you are reading from a ListBox inside a bgw, that is not thread-safe. Even if it may appear to work, when the ListBox changes you could get null values or exceptions. Very infrequently.
Edit
And going the other way is even easier:
int i = -1;
for(;;) // or while(true)
{
i = (i + 1) % listBox1.Items.Count;
if (worker.CancellationPending)
{
...
}
}
Something like this ?
BackgroundWorker worker = sender as BackgroundWorker;
bool go = true;
while(go)
{
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
{
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
go = false;
break;
}
else
{
string queryhere = listBox1.Items[i].ToString();
this.SetTextappend("" + queryhere + "\n");
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 1);
}
}
}
}
int N = listbox1.Items.Count;
for (int i=N-1; !worker.CancellationPending; i = (i+N-1) % N )
{
// this weird calculation i=(i+N-1)%N is because I'm not sure whether C#
// divides negative integers properly (so that the remainder is non-negative)
// It could be i=(i-1)%N .
string queryhere = listBox1.Items[i].ToString();
this.SetTextappend("" + queryhere + "\n");
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 1); // by the way, there should be a percentage.
// You better revise the progress reporting.
}
e.Cancel = true;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (worker.CancellationPending != true)
{
for (int i = listBox1.Items.Count - 1; i >= 0; i--)
{
if(worker.CancellationPending != true)
{
string queryhere = listBox1.Items[i].ToString();
this.SetTextappend("" + queryhere + "\n");
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 1);
}
else
{
break;
}
}
}
e.Cancel = true;
}