|
Part of my application uploads files to an FTP server and reports progress. Currently, the progress is simply calculated by number of files remaining vs. files total in order to generate a percentage and update a progress bar. I want to detail out a little bit more and have a file upload progress bar as well as an overall progress bar.
The entire operation is on a background worker, upload is a function within the process, not on it's own thread. Do I need to employ another background worker in order to update the file progress bar (Which would update at byte chunk intervals) while the main background worker updates the overall progress bar (Which updates on a per file basis)?
Would it be better to calculate the total bytes of all files and then update both progress bars at the same time with the percentage of progress on the overall bar being bytes sent vs. total bytes while the file bar being file bytes sent vs. total file bytes?
I think I answered my own question there. But I would like to know what others think.
The other question is this: I am storing the names of the files to be uploaded in a file queue DB table and accessing it with a data reader. In order to calculate the total size of all files do you think it would be better to store the file size of those files in the table at the time they are inserted and run a simple SUM() operation when I get the data in the first place? Or perhaps write a routine that loops the datareader once and calculates the total file(s) size with an IO operation?
Thank you very much, --EA
|
|
|
|
|
Hi EA,
1.
BackgroundWorker.ReportProgress() takes either an int, or an int and an object of your choice.
So you could pass two ints to the ProgressChanged event, where the second one would get boxed.
Or you could create a class or struct holding whatever you want to pass, and just pass that.
Or you could use one or more class members.
The only thing you must keep in mind is two threads are active here: the BGW making progress, and the GUI thread displaying progress; so you must avoid inconsistent progress data; not by using locks, but in some other way, e.g. by using value types.
An alternative would be this:
pass the progress within a file as the int, and pass the filename as the object;
let the display routine figure out the overall progress, which can be organized to require only a very simple addition and division.
2.
if the file sizes don't change (much), I would consider storing them in the table; getting them from the file system may be too expensive, depends on how many and how far off (network drive, FTP site, ...).
3.
Warning: don't update progress for every tiny amount of data handled, since that might end up slowing it all down. Updating more than say 20 times per second really makes no sense, the human eye won't follow.
Occasionally I update progress by polling rather than pushing it out: just have a timer (a System.Windows.Forms.Timer is the best fit) cause the display to be updated periodically.
|
|
|
|
|
Thank you Luc, you are a well of knowledge. Do you think there is value to creating a nested class vs. an on the fly array?
Example:
protected class myHandler
{
public int fileProgress;
public int overallProgress;
public object Clone();
}
bgWorker.reportProgress(0,myHandler.Clone());
vs.
Int32[] myProgress = new Int32[];
bgWorker.reportProgress(0, myProgress);
I had some ongoing errors with the protected handler class, so right now I am using the on the fly Int array, but if it is just plain wrong...
|
|
|
|
|
If you only have two progress bars, one overall, one for current file, all you need to pass is two percentages. So a simple struct, or an int[2] array would do. However, I would be inclined to make it even simpler:
foreach (string filename in filenames) {
ReportProgress(101, filename);
... some kind of loop {
...
ReportProgress(percentage, filename);
}
ReportProgress(102, filename);
}
and let the ProgressChanged event do the math:
long totalDone;
long overallTotal;
Directory<string filename, long filesize> namesToSizes;
void ProgressChanged(object sender, ProgressChangedEventArgs e) {
int p=e.ProgressPercentage;
string filename=e.UserState;
long filesize=namesToSizes[filename];
if (p==101) {
labelIndividual.Text="now dealing with file "+filename;
} else if (p==102) {
totalDone+=filesize;
} else {
ProgressBarIndividual.Value=p;
ProgressBarTotal.Value=(totalDone+filesize*p/100)*100/overallTotal;
}
}
You have to set up overallTotal and namesToSizes before entering the loop.
Note: While the documentation calls the first parameter "percentProgress", it is not limited to the range [0, 100] so I used two values outside that range to request special actions.
|
|
|
|
|
That is some great information all the way around. Mission accomplished, thanks Luc!
--EA
|
|
|
|
|
Hi all,
I have the following code that I need to return dblRx our of DisplayText in order to use it in another function. Problem I'm having is that if I try to return a double from DisplayText (private double DisplayText), I get the EventHandler error from this.Invoke that I have the wrong data type. Any help would be appreciated.
private void DisplayText(object sender, EventArgs e)
{
RxString = RxString.Remove(0, 3);
double dblRx = double.Parse (RxString);
RxString = dblRx.ToString();
textBox1.Text = RxString;
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = "";
try
{
RxString = serialPort1.ReadTo("\r");
serialPort1.WriteLine("*X01\r");
}
catch (Exception Exception)
{
return;
}
this.Invoke(new EventHandler(DisplayText));
}
|
|
|
|
|
Not sure I understand your question very well, however here are some remarks:
1.
mprice214 wrote: RxString.Remove(0, 3); //This removes the first 4 characters
Not true. At best it removes 3 characters; and if there are fewer, expect an exception.
2.
mprice214 wrote: double.Parse (RxString);
that will fail if the data isn't really a string representation of a double. Don't do this on external data, it will bite you. Either add try-catch or better yet use TryParse().
3.
why does DisplayText() have parameters which make it look like a real event handler, you are not using those parameters, instead you are using some class member. It would seem logical to give it one parameter: the text it should display.
4.
mprice214 wrote: serialPort1.WriteLine("*X01\r");
very strange. You want a carriage return plus whatever the system uses for a newline (maybe "\r\n"). Either you like what the system gives you, or you don't; I would never mix the two.
5.
mprice214 wrote: catch (Exception Exception)
{
return;
}
This is not acceptable; whatever goes wrong, you ignore it. So when it goes wrong, you will have an impossible job to diagnose and fix the problem. And believe me, serial communication always goes wrong sooner or later. You should NOT swallow exceptions, either catch a very specific one and add a comment as to why you really don't care about it, or log it somewhere so it leaves a trail.
6.
mprice214 wrote: this.Invoke(new EventHandler(DisplayText));
That does not work, as the parameter list does not match. First make sure which parameters the method should have, then make sure to provide them, using an overload of Invoke().
|
|
|
|
|
Luc Pattyn wrote: maybe "\r\n"
How about using Environment.NewLine ?
|
|
|
|
|
If he wants "\r\n" then that is what he should set as SerialPort.NewLine
One should not rely on Environment.NewLine for strings that go beyond the current system (files that get exported, serial communication, etc).
BTW: MSDN is confusing about the default value of SerialPort.NewLine (it says it is "\n" but refers to Environment.NewLine)
|
|
|
|
|
Thanks.
|
|
|
|
|
you're welcome.
|
|
|
|
|
I use it for most things, but when communicating externally I tend to create a constant set to whatever value needs to be defined and use that. I've recently been writing an IMAP thing for work - it's in the RFC that it uses \r\n at the end of every command so that's what I use, just in case it ever ends up being ported to a system that doesn't use it!
DaveIf this helped, please vote & accept answer!
Binging is like googling, it just feels dirtier. (Pete O'Hanlon)
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
|
|
|
|
|
1.
mprice214 wrote:
RxString.Remove(0, 3); //This removes the first 4 characters
Not true. At best it removes 3 characters; and if there are fewer, expect an exception.
Yes, understand. I originally was removing 4.
2.
mprice214 wrote:
double.Parse (RxString);
that will fail if the data isn't really a string representation of a double. Don't do this on external data, it will bite you. Either add try-catch or better yet use TryParse().
Understand this too
3.
why does DisplayText() have parameters which make it look like a real event handler, you are not using those parameters, instead you are using some class member. It would seem logical to give it one parameter: the text it should display.
see #6
4.
mprice214 wrote:
serialPort1.WriteLine("*X01\r");
very strange. You want a carriage return plus whatever the system uses for a newline (maybe "\r\n"). Either you like what the system gives you, or you don't; I would never mix the two.
The device only requires a cr and as result, it is fine with just \r or \r and \n. Why would a newline be necessary here?
5.
mprice214 wrote:
catch (Exception Exception)
{
return;
}
This is not acceptable; whatever goes wrong, you ignore it. So when it goes wrong, you will have an impossible job to diagnose and fix the problem. And believe me, serial communication always goes wrong sooner or later. You should NOT swallow exceptions, either catch a very specific one and add a comment as to why you really don't care about it, or log it somewhere so it leaves a trail.
Understand. I am trying to get things working first and then am going back to clean up. As I'm relatively new to c#, I'm sure I'll miss some things.
6.
mprice214 wrote:
this.Invoke(new EventHandler(DisplayText));
That does not work, as the parameter list does not match. First make sure which parameters the method should have, then make sure to provide them, using an overload of Invoke().
When I run this, it works. Correct me if I am wrong, but doesn't this pass RxSring back to DisplayText? Are you saying there is a better way to do this?
Regarding the initial question, I need to pass RxString out of DisplayText as a double to use for graphing purposes.
|
|
|
|
|
mprice214 wrote: The device only requires a cr
so use serialPort1.Write("*X01\r"); instead of WriteLine() and you're in charge, now it doesn't matter what SerialPort.NewLine contains any more.
mprice214 wrote: get things working first
sure, so am I. By seeing exceptions right away. As a minimum, add Console.WriteLine(exception.ToString()); . Either you don't have exceptions, then it makes no difference; or you have, and now you can see them.
mprice214 wrote: it works
Yes, I forgot: Invoke without parameters provides some defaults, and since you don't really use the parameters inside DisplayText, there is no problem. However, I wouldn't do it like that.
mprice214 wrote: Regarding the initial question
OK, if you are not really interested in the string, why not use a class member "double Rx;" instead of "string RxString;". Have DataReceived read the string, convert it to double, and store the value in Rx; then have DisplayText and any other interested method just read the Rx variable.
|
|
|
|
|
Luc Pattyn wrote: Yes, I forgot: Invoke without parameters provides some defaults, and since you don't really use the parameters inside DisplayText, there is no problem. However, I wouldn't do it like that.
Do you have any other suggestions?
Luc Pattyn wrote: OK, if you are not really interested in the string, why not use a class member "double Rx;" instead of "string RxString;". Have DataReceived read the string, convert it to double, and store the value in Rx; then have DisplayText and any other interested method just read the Rx variable.
Thank you for this and also the the comment on the exceptions!
|
|
|
|
|
you're welcome.
mprice214 wrote: any other suggestions?
yes, if you need parameters, use the Invoke() overload that takes an array of objects.
|
|
|
|
|
mprice214 wrote: this.Invoke(new EventHandler(DisplayText));
Has some paramter issues, to get around it without changing too much code try this
this.Invoke(new MethodInvoker(delegate{DisplayText(null, null);}));
|
|
|
|
|
I'm not sure why this would need to change, as it seems to work fine. I'm apparently missing something here. BTW, RxString is global.
|
|
|
|
|
I need help. I want to save the datatable to a *.xls file. wBook.FileFormat is only get Attribute.
How to set the file type to *.xls?
<code>
Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
try
{
app.Visible = false;
Excel.Workbook wBook = app.Workbooks.Add(true);
Excel.Worksheet wSheet = wBook.Worksheets[1] as Excel.Worksheet;
if (dt.Rows.Count > 0)
{
int row = 0;
row = dt.Rows.Count;
int col = dt.Columns.Count;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
string str = dt.Rows[i][j].ToString();
wSheet.Cells[i + 2, j + 1] = str;
}
}
}
int size = dt.Columns.Count;
for (int i = 0; i < size; i++)
{
wSheet.Cells[1, 1 + i] = dt.Columns[i].ColumnName;
}
app.DisplayAlerts = false;
app.AlertBeforeOverwriting = false;
wBook.Save();
app.Save(filePath);
app.SaveWorkspace(filePath);
app.Quit();
app = null;
}
catch (Exception err)
{
return -1;
}
finally
{
}
</code>
modified on Friday, April 16, 2010 8:07 AM
|
|
|
|
|
so you are trying to save in *.xls format from C# by using Interop. But you have excel 2007 wright?
If that's the case a little trick:
record a macro that stores the save as 2003
ActiveWorkbook.SaveAs Filename:= _
"C:\Users\dan.mos\Desktop\New Microsoft Office Excel Worksheet (2).xls", _
FileFormat:=xlExcel8, Password:="", WriteResPassword:="", _
ReadOnlyRecommended:=False, CreateBackup:=False
and convert it to c# code. It works like a charm.
As a sugestion your cell by cell approach is really slow especially for large amounts of data.
Have a look here or
here.
|
|
|
|
|
Hi guys,
I have a datagrid that includes a checkbox in each row...
Does anyone know how I can check which rows have a checked check box?
and retrieve a certain value(say mobile number)
Please help
Here is what i have tried:
CheckBox chkSelection;
foreach (DataGridItem item in contacts.Items)
{
chkSelection = (CheckBox)item.FindControl("chkSelection");
if (chkSelection.Checked == true)
{
Control contact = (Control)item.FindControl("phone_num");
errors0.Text += contact.ToString();
errors0.Visible = true;
}
}
No this won't work
|
|
|
|
|
Hi dear,
please specify what errors0 means. well try one thing change the datatype I suppose phone_num is a text box so cast it to textbox and then try to have its value using tostring.
<br />
if (chkSelection.Checked == true) { <br />
TextBox contact = (TextBox)item.FindControl("phone_num");<br />
errors0.Text += contact.ToString(); <br />
errors0.Visible = true;
}<br />
|
|
|
|
|
Hi there thanks for my reply.
well, phone_nume is a column in the datagride that holds phone numbers
and chkSelection is a checkbox id that is in the same datagrid.
I would like to retrieve these phone numbers where a checked is = true...
Do you know how you can do this?
Oops! errors0 is just a demo Label to display the numbers after retrieving them
from my datagrid..
Please help if you can...
|
|
|
|
|
Hi,
I develop a Csharp smart device application.I was in the first step.I create a ConnexionForm where user writes his login and password,the system verifys if they are in the database .sdf.I write this code:
private void button1_Click(object sender, EventArgs e)
{
string s1 = textBox1.Text.ToString();
string s2 = textBox2.Text.ToString();
BaseGmaoLocaleDataSet2 dat = new BaseGmaoLocaleDataSet2();
DataRow[] foundRows;
foundRows = dat.Tables["Connexion"].Select("Login like s1 and MotPasse like s2");
if (foundRows != null)
{
MessageBox.Show("Authentification réussie");
MenuP m = new MenuP();
m.Show();
}
else
{
MessageBox.Show("Login ou mot de passe incorrect veuillez réessayer");
}
}}}
But an error appears to me telling me that there is an error in the form of the 2 strings s1 et S2
Can you help me?
Thanks for all u suggestions
|
|
|
|
|
Hi,
several comments:
1.
your database does not know what s1 and s2 mean in Select("Login like s1 and MotPasse like s2");
you want the content of s1 and s2, not the names of the variables; so they must not be inside double quotes.
and then you want SQL to see them as string literals, which requires single quotes.
So at least you should change it to
...Select("Login like '"+s1+"' and MotPasse like '"+s2+"'");
2.
it does not make sense to use like like that, there are no wildcards, nor anything special. So better write
...Select("Login = '"+s1+"' and MotPasse = '"+s2+"'");
3.
you should not pass user input straight to an SQL statement, it makes your app very vulnerable; the user could type things that end up your SQL statement do things you don't want such as delete a table.
Either check your inputs (you must avoid empty fields too!) or use parameterized SQL (use SQLParameter).
4.
You should not store plain passwords in a database; you should use encryption or hashing. Read up on best practices for passwords!
|
|
|
|
|