I am using C# and the WebClient class.
This is the code I am using, inspired by another post here on SO. This code worked well for large files, it accurately displayed the download speed. However, there is now the limitation of downloading individual files, many of which are small, with small being .5-5 MB. This has caused the speed counter to skyrocket, often into the hundreds of thousands of KBps. I'm not sure what else to try to combat this. I added a second progress bar showing individual file downloads which helps improve the image a bit, but the download speed counter should really be fixed. Is there a different class to use that would solve this problem?
The WebClient in this code is disposed of properly elsewhere.
private class NetSpeedCounter
{
private double[] DataPoints;
private DateTime LastUpdate;
private int NumCounts = 0;
private int PrevBytes = 0;
public double Speed { get; private set; }
public NetSpeedCounter(WebClient webClient, int maxPoints = 10)
{
DataPoints = new double[maxPoints];
Array.Clear(DataPoints, 0, DataPoints.Length);
webClient.DownloadProgressChanged += (sender, e) =>
{
var msElapsed = DateTime.Now - LastUpdate;
int curBytes = (int)(e.BytesReceived - PrevBytes);
PrevBytes = (int)e.BytesReceived;
double dataPoint = ((double)curBytes) / msElapsed.TotalSeconds;
DataPoints[NumCounts++ % maxPoints] = dataPoint;
Speed = DataPoints.Average();
};
}
public void Reset()
{
PrevBytes = 0;
LastUpdate = DateTime.Now;
}
}
I download the files with this code, which is started afterwards by a call to DownloadFileAsync. This code just downloads them in a chain, one after another, asynchronously.
This is setting up for starting the download
Queue recordQ = new Queue(files);
progressBar.Value = 0;
progressBar.Maximum = recordQ.Count;
UpdateStatusText("Downloading " + recordQ.Count + " files");
var record = recordQ.Dequeue();
speedUpdater.Start();
CheckAndCreate(record.AbsolutePath);
Adding the event handler
wc.DownloadFileCompleted += (sender, e) =>
{
var nr = recordQ.Dequeue();
CheckAndCreate(nr.AbsolutePath);
this.Invoke((MethodInvoker)delegate
{
UpdateStatusText("Downloading " + recordQ.Count + " files", lblStatusR.Text);
});
counter.Reset();
// download the next one
wc.DownloadFileAsync(nr.DownloadPath, nr.AbsolutePath);
}
counter.Start();
wc.DownloadFileAsync(record.DownloadPath, record.AbsolutePath);
This last call is what starts everything off.
DateTime.Now is not accurate enough for some scenarios where timespans are recorded frequently(Eric Lippert mentions that they have a precision of 30 ms here), since DateTime.Now will return the previously used DateTime.Now when called quickly in succession. This might result in discrepencies in your speed counter due to inaccurate increases when downloads are finished very quickly. I'd recommend using StopWatch API for that purpose.
EDIT
I have created the following test Winforms application based on your code that works fine for small files. I'm getting a reasonable 200 kbps over my intranet for 5 files that are about 2MB each. Just make sure you're calling the stopwatch classes at the right places.
To replicate, create a winforms app, create 3 labels of Id lblSpeed, lblStatus, lblFile and copy\paste the code and rename the URI's below to the files you want to test on.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Queue<Record> recordQ;
WebClient wc;
NetSpeedCounter counter;
//We store downloaded files in C:\TestDir (hardcoded in the Record class below)
public Form1()
{
InitializeComponent();
recordQ = new Queue<Record>();
//replace the URI string below. Nothing else to replace.
//recordQ.Enqueue(new Record(#"URI1", "SQLtraining.exe"));
//recordQ.Enqueue(new Record(#"URI2", "Project Mgmt.pptx"));
//first uri to process. Second param is the file name that we store.
Record record = new Record(#"URI0","Agile.pptx"); // replace the URI
//Initialize a webclient and download the first record
using (wc = new WebClient())
{
counter = new NetSpeedCounter(wc);
wc.DownloadFileCompleted += (sender, e) =>
{
if (recordQ.Count == 0)
{
UpdateStatusText("Done");
return;
}
var nr = recordQ.Dequeue();
//just create directory. the code uses the same directory
CheckAndCreate(nr.Directory);
//need not even use invoke here. Just a plain method call will suffice.
this.Invoke((MethodInvoker)delegate
{
UpdateStatusText("Left to process: " + recordQ.Count + " files");
});
counter.Reset();
counter.Start();
//continue with rest of records
wc.DownloadFileAsync(nr.DownloadPath, nr.GetFullPath());
this.lblFile.Text = nr.DownloadPath.OriginalString;
};
//just update speed in UI
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
counter.Start();
//display URI we are downloading
this.lblFile.Text = record.DownloadPath.OriginalString;
//start first download
wc.DownloadFileAsync(record.DownloadPath, record.GetFullPath());
}
}
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.lblSpeed.Text = counter.Speed.ToString();
}
public void UpdateStatusText(string msg)
{
this.lblStatus.Text = msg;
}
public void CheckAndCreate(string absPath)
{
if (!Directory.Exists(absPath))
Directory.CreateDirectory(absPath);
}
}
public class NetSpeedCounter
{
private int NumCounts = 0;
private int PrevBytes = 0;
private Stopwatch stopwatch;
public double Speed { get; private set; }
double[] DataPoints;
public NetSpeedCounter(WebClient webClient, int maxPoints = 10)
{
DataPoints = new double[maxPoints];
stopwatch = new Stopwatch();
Array.Clear(DataPoints, 0, DataPoints.Length);
webClient.DownloadProgressChanged += (sender, e) =>
{
var msElapsed = DateTime.Now - LastUpdate;
stopwatch.Stop();
int curBytes = (int)(e.BytesReceived - PrevBytes);
PrevBytes = (int)e.BytesReceived;
//record in kbps
double dataPoint = (double)curBytes / (stopwatch.ElapsedMilliseconds);
DataPoints[NumCounts++ % maxPoints] = dataPoint;
//protect NumCount from overflow
if (NumCounts == Int32.MaxValue)
NumCounts = 0;
Speed = DataPoints.Average();
stopwatch.Start();
};
}
public void Start()
{
stopwatch.Start();
}
public void Reset()
{
PrevBytes = 0;
stopwatch.Reset();
}
}
public class Record
{
public string Directory;
public string File;
public Uri DownloadPath;
public Record(string uriPath, string fileOutputName)
{
this.Directory = #"C:\TestDir\";
this.DownloadPath = new Uri(uriPath);
this.File = fileOutputName;
}
public string GetFullPath()
{
return this.Directory + this.File;
}
}
}
I have come up with a different way of doing it.
Instead of adding a data point for each ProgressChanged event, I now increment a counter with the amount of bytes. This counter stays across multiple files, and then I simply take a datapoint using a timer, and get the time in between using a System.Diagnostic.Stopwatch
It works very good, the accuracy is much much better, I would highly recommend doing it this way.
Here is some code
class NetSpeedCounter
{
private Stopwatch watch;
private long NumCounts = 0;
private int PrevBytes = 0;
private double[] DataPoints;
private long CurrentBytesReceived = 0;
public double Speed { get; private set; }
private System.Timers.Timer ticker = new System.Timers.Timer(100);
public NetSpeedCounter(WebClient webClient, int maxPoints = 5)
{
watch = new System.Diagnostics.Stopwatch();
DataPoints = new double[maxPoints];
webClient.DownloadProgressChanged += (sender, e) =>
{
int curBytes = (int)(e.BytesReceived - PrevBytes);
if (curBytes < 0)
curBytes = (int)e.BytesReceived;
CurrentBytesReceived += curBytes;
PrevBytes = (int)e.BytesReceived;
};
ticker.Elapsed += (sender, e) =>
{
double dataPoint = (double)CurrentBytesReceived / watch.ElapsedMilliseconds;
DataPoints[NumCounts++ % maxPoints] = dataPoint;
Speed = DataPoints.Average();
CurrentBytesReceived = 0;
watch.Restart();
};
}
public void Stop()
{
watch.Stop();
ticker.Stop();
}
public void Start()
{
watch.Start();
ticker.Start();
}
public void Reset()
{
CurrentBytesReceived = 0;
PrevBytes = 0;
watch.Restart();
ticker.Start();
}
}
Related
I'm building an auto clicker application that makes the mouse click at a set interval of time.
Notice the interval configuration area. I have tried to write some logic that automatically simplifies the amount of time entered. The minutes box goes up to 60, the seconds box goes up to 60, and the milliseconds box goes up to 1000. I have a class set up to handle that logic, but it's probably not the right way to do that (I'm still new at programming).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AutoClicker {
public static class TotalTime {
public static int Interval(NumericUpDown m_, NumericUpDown s_, NumericUpDown ms_) {
int m = (int)m_.Value;
int s = (int)s_.Value;
int ms = (int)ms_.Value;
int total = 0;
total = total + (m * 60000);
total = total + (s * 1000);
total = total + ms;
return total;
}
public static void ChangeLogic(NumericUpDown m_, NumericUpDown s_, NumericUpDown ms_) {
int interval = Interval(m_, s_, ms_);
if (ms_.Value == 1000)
{
ms_.Value = 500;
s_.UpButton();
ms_.Value = 0;
}
if (s_.Value == 60 && m_.Value < 60)
{
if (ms_.Value == 0)
{
ms_.Value = 1;
s_.Value = 0;
m_.UpButton();
ms_.Value = 0;
}
else
{
s_.Value = 0;
m_.UpButton();
if (ms_.Value == 1)
ms_.DownButton();
}
}
if (ms_.Value == -1)
{
ms_.Value = 999;
s_.DownButton();
}
if (s_.Value == -1 & m_.Value > 0)
{
s_.Value = 59;
m_.DownButton();
if (ms_.Value == 1)
ms_.DownButton();
}
}
}
}
The ChangeLogic method is called each time the value in any of the boxes is updated.
There's lots of bugs in the selection logic. For example, when the up button for seconds is held down, the program crashes. If seconds = 59 and minutes = 0, and the seconds up button is pressed, the warning "you can't have a time less than 250 milliseconds" shows up and minutes is adjusted to 2.
I'm really confused. Can anyone help me? Thanks!
So, it looks like your intent is to have it so that when you up the milliseconds it then rolls up to the seconds once it hits 1000, then when your seconds hit 60 it rolls up to your minutes, etc etc.
Here is how I would accomplish this. First set all of your NUDs to max value of 99999. Then use code that looks something like this:
(Note the use of the intervalUpdating variable. This will make it so that you can hold that up button and while the intervalChanged() method is executing it wont try to execute it again.)
private void nudSeconds_ValueChanged(object sender, EventArgs e)
{
intervalChanged();
}
private void nudMilliseconds_ValueChanged(object sender, EventArgs e)
{
intervalChanged();
}
private bool intervalUpdating = false;
private void intervalChanged()
{
if (intervalUpdating)
{
return;
}
intervalUpdating = true;
if (nudMilliseconds.Value >= 1000)
{
var val = (int)nudMilliseconds.Value / 1000;
nudSeconds.Value += val;
nudMilliseconds.Value = (nudMilliseconds.Value - (val * 1000));
}
if (nudSeconds.Value >= 60)
{
var val = (int)nudSeconds.Value / 60;
nudMinutes.Value += val;
nudSeconds.Value = (nudSeconds.Value - (val * 60));
}
intervalUpdating = false;
}
I´m currently trying to add parallel downloads to my application but I don´t know how to handle the DownloadProgressChangedEvent to display the progress in multiple progressbars.
I´m using a datagridview with predefined rows for each file the user is able to download and each row has a cell with a progressbar in it.
The problem now is, that I don´t know how to update each progressbar individually, because right now, all selected progressbars are showing the same percentage and they´re just jumping between the progress of download1 & download2.
Here´s the code im using:
To start the downloads:
private void download_button_Click(object sender, EventArgs e)
{
start = DateTime.Now;
download_button.Enabled = false;
Rows = dataGridView1.Rows.Count;
Checked = 0;
CheckedCount = 0;
//count the selected rows
for (i = 0; i < Rows; i++)
{
Checked = Convert.ToInt32(dataGridView1.Rows[i].Cells["checkboxcol"].FormattedValue);
CheckedCount += Checked;
richTextBox3.Text = CheckedCount.ToString();
}
for (int z = 1; z < CheckedCount; z++)
{
_MultipleWebClients = new WebClient();
_MultipleWebClients.DownloadFileCompleted += new AsyncCompletedEventHandler(_DownloadFileCompleted);
_MultipleWebClients.DownloadProgressChanged += new System.Net.DownloadProgressChangedEventHandler(_DownloadProgressChanged);
_MultipleWebClients.DownloadFileAsync(new Uri(_downloadUrlList[z].ToString()), #"F:\test" + z + ".mp4");
}
}
(I´m also unable to download more than two files simultaneously - the third download won´t start until the first two are finished)
DownloadProgressChangedEvent:
private void _DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
for (int c = 0; c < CheckedCount; c++)
{
dataGridView1.Rows[_downloadRowNrList[c]].Cells[3].Value = e.ProgressPercentage;
}
float size = ((e.TotalBytesToReceive / 1024) / 1024);
label1.Text = size.ToString();
double dn = (double)e.BytesReceived / 1024.0 / (DateTime.Now - start).TotalSeconds;
label2.Text = (dn.ToString("n") + " KB/s) " + e.ProgressPercentage);
}
The problem probably is, that all progressbars are using the same DownloadProgressChangedEvent, but I´m not sure how to create multiple of these events without knowing the needed number...
So i hope that someone is able to help me with this,
thanks in advance!
What you want to do is use the other DownloadFileAsync method:
http://msdn.microsoft.com/en-us/library/ms144197.aspx
The third parameter is a userToken which gets passed as part of the DownloadProgressChangedEventArgs (it's in the UserState property).
So, when you make the DownloadFileAsync call, pass in a unique token (an integer, or something else) that you can then associate with the progressBar that needs updating.
//(Snip)
//in download_button_Click, pass the row you are updating to the event.
for (int z = 1; z < CheckedCount; z++)
{
_MultipleWebClients = new WebClient();
_MultipleWebClients.DownloadFileCompleted += new AsyncCompletedEventHandler(_DownloadFileCompleted);
_MultipleWebClients.DownloadProgressChanged += new System.Net.DownloadProgressChangedEventHandler(_DownloadProgressChanged);
_MultipleWebClients.DownloadFileAsync(new Uri(_downloadUrlList[z].ToString()), #"F:\test" + z + ".mp4", dataGridView1.Rows[z]);
}
}
private void _DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
var rowToUpdate = (DataGridViewRow)e.UserState;
RowToUpdate["ProgressBar"].Value = e.ProgressPercentage;
RowToUpdate["TextProgress"].Value = e.ProgressPercentage;
RowToUpdate["BytesToRecive"].Value = ((e.TotalBytesToReceive / 1024) / 1024).ToString();
double dn = (double)e.BytesReceived / 1024.0 / (DateTime.Now - start).TotalSeconds;
RowToUpdate["Speed"].Value = (dn.ToString("n") + " KB/s) " + e.ProgressPercentage);
}
Sounds like you need a progress bar for multi-parted progress:
public partial class ProgressBarEx : ProgressBar
{
private readonly Dictionary<Guid, double> _partsProgress =
new Dictionary<Guid, double>();
private readonly Dictionary<Guid, double> _partsSizes =
new Dictionary<Guid, double>();
private double _value;
private double _maximum;
public ProgressBarEx()
{
this.InitializeComponent();
}
public int Parts
{
get { return this._partsSizes.Count; }
}
public new int Minimum { get; private set; }
public new double Maximum
{
get { return this._maximum; }
private set
{
this._maximum = value;
base.Maximum = (int)value;
}
}
public new double Value
{
get { return this._value; }
private set
{
this._value = value;
base.Value = (int)value;
}
}
[Obsolete("Not useable in ProgressBarEx.")]
public new int Step
{
get { return 0; }
}
public Guid AddPart(double size)
{
if (size <= 0)
{
throw new ArgumentException("size");
}
var partId = Guid.NewGuid();
this.Maximum += size;
this._partsSizes.Add(partId, size);
this._partsProgress.Add(partId, 0);
return partId;
}
public bool RemovePart(Guid partId)
{
double size;
if (!this._partsSizes.TryGetValue(partId, out size))
{
return false;
}
this.Maximum -= size;
this._partsSizes.Remove(partId);
this.Value -= this._partsProgress[partId];
this._partsProgress.Remove(partId);
return true;
}
public bool ContainsPart(Guid partId)
{
return this._partsSizes.ContainsKey(partId);
}
public double GetProgress(Guid partId)
{
return this._partsProgress[partId];
}
public void SetProgress(Guid partId, double progress)
{
if (progress < 0 || this._partsSizes[partId] < progress)
{
throw new ArgumentOutOfRangeException("progress");
}
this.Value += progress - this._partsProgress[partId];
this._partsProgress[partId] = progress;
}
public void AddProgress(Guid partId, double progress)
{
this.SetProgress(partId, progress + this._partsProgress[partId]);
}
[Obsolete("Not useable in ProgressBarEx.")]
public new void PerformStep()
{
}
}
Example usage:
public Form1()
{
InitializeComponent();
var pbe = new ProgressBarEx {Location = new Point(100, 100)};
this.Controls.Add(pbe);
for (var i = 0; i < 4; i++)
{
var size = i * 10 + 30;
var partId = pbe.AddPart(size);
var pb = new ProgressBar
{
Maximum = size,
Location = new Point(100, i * 30 + 130)
};
this.Controls.Add(pb);
var timer = new Timer {Interval = 1000 + i * 100};
timer.Tick += (sender, args) =>
{
pb.Value += 5;
pbe.AddProgress(partId, 5);
if (pb.Value == pb.Maximum)
{
timer.Stop();
}
};
timer.Start();
}
}
I wrote this for a meteorology class. For some reason, the textboxes aren't updating on the GUI correctly for large numbers (large numbers of photons). The calculation completes, but the textboxes don't update.
I suspect the problem is with calling Invoke(), but I can't for the life of me see what's going wrong. I've tried using both Invoke() and BeginInvoke() with similar results.
Can anyone help figure out where I'm going wrong?
Thanks!
PS> Please forgive the global variables. Was planning on cleaning them up later...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace CloudTransmittance
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonCalculate_Click(object sender, EventArgs e)
{
Thread t = new Thread(calculateModel);
t.Start();
}
//number of photons that have gone to albedo, direct, or diffuse transmittance
private ulong top = 0;
private ulong direct = 0;
private ulong diffuse = 0;
private ulong absorbed = 0;
private ulong failed = 0;
private ulong photons = 0;
private void calculateModel()
{
//model variables
double theta = 0;
double tauStar = 0;
double omega = 0;
double g = 0;
photons = 0;
//Get data from form
theta = Convert.ToDouble(textBoxTheta.Text);
tauStar = Convert.ToDouble(textBoxTau.Text);
omega = Convert.ToDouble(textBoxOmega.Text);
g = Convert.ToDouble(textBoxG.Text);
photons = Convert.ToUInt64(textBoxPhotons.Text);
//Clear the progress bar and set its limits
this.progressBar1.BeginInvoke(
(MethodInvoker)delegate()
{
this.progressBar1.Minimum = 0;
this.progressBar1.Value = 0;
this.progressBar1.Maximum = (int)photons;
this.progressBar1.Step = 1;
});
//Clear the text boxes
this.textBoxAlbedo.Invoke(
(MethodInvoker)delegate()
{
this.textBoxAlbedo.Text = "";
});
this.textBoxDirect.Invoke(
(MethodInvoker)delegate()
{
this.textBoxDirect.Text = "";
});
this.textBoxDiffuse.Invoke(
(MethodInvoker)delegate()
{
this.textBoxDiffuse.Text = "";
});
this.textBox1.Invoke(
(MethodInvoker)delegate()
{
this.textBox1.Text = "";
});
this.textBox2.Invoke(
(MethodInvoker)delegate()
{
this.textBox2.Text = "";
});
//convert theta to radians from degrees
theta *= Math.PI / 180;
//number of photons that have gone to albedo, direct, or diffuse transmittance
top = 0;
direct = 0;
diffuse = 0;
absorbed = 0;
failed = 0;
//Random number generator
Random r = new Random();
double randomValue = 0;
int count = 1000; //number of iterations of the problem...
double delta = 0.00001; //close enough to "1" for calculations, since C# random goes from [0, 1) instead of [0, 1]
//Calculate transmittance
for (ulong photonCount = 0; photonCount < photons; photonCount++)
{
bool scattered = false;
double newTheta = theta; //needed for looping
int i = 0; //counting variable used to prevent infinite looping
for (i = 0; i < count; i++)
{
double length = calculateTauP(); //length of the photon's travel
double newTau = calculateTau(newTheta, length);
if (newTau < 0)
{
top++; //photon has exited through the top
break; //move to the next photon
}
else if (newTau > tauStar)
{
//exited through the bottom of the cloud
if (scattered == false)
{
//direct transmittance
direct++;
}
else
{
//diffuse transmittance
diffuse++;
}
break;
}
else
{
//photon is either scattered or absorbed
randomValue = r.NextDouble();
if (randomValue >= omega) // || ((omega == 1) && (randomValue >= (omega - delta)) )
{
//photon absorbed, no longer of interest
absorbed++;
break;
}
else
{
//photon scattered, determine direction
scattered = true;
newTheta = calculateNewAngle(newTau, newTheta, g, randomValue);
}
}
}
if (i >= count)
{
failed++;
}
this.progressBar1.BeginInvoke(
(MethodInvoker)delegate()
{
this.progressBar1.PerformStep();
});
}
//Update Form values
displayData();
}
private void displayData()
{
if (this.textBoxAlbedo.InvokeRequired)
{
this.textBoxAlbedo.Invoke(
(MethodInvoker)delegate()
{
this.textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
});
}
else
{
textBoxAlbedo.Text = ((double)top / (double)photons).ToString();
}
if (this.textBoxDirect.InvokeRequired)
{
this.textBoxDirect.Invoke(
(MethodInvoker)delegate()
{
this.textBoxDirect.Text = ((double)direct / (double)photons).ToString();
});
}
else
{
textBoxDirect.Text = ((double)direct / (double)photons).ToString();
}
if (this.textBoxDiffuse.InvokeRequired)
{
this.textBoxDiffuse.Invoke(
(MethodInvoker)delegate()
{
this.textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
});
}
else
{
textBoxDiffuse.Text = ((double)diffuse / (double)photons).ToString();
}
if (this.textBox1.InvokeRequired)
{
this.textBox1.Invoke(
(MethodInvoker)delegate()
{
this.textBox1.Text = absorbed.ToString();
});
}
else
{
textBox1.Text = absorbed.ToString();
}
if (this.textBox2.InvokeRequired)
{
this.textBox2.Invoke(
(MethodInvoker)delegate()
{
this.textBox2.Text = failed.ToString();
});
}
else
{
textBox2.Text = failed.ToString();
}
}
private double calculateNewAngle(double length, double angle, double g, double randomNumber)
{
double newAngle = 0;
double cos = (1 / (2 * g)) * (1 + Math.Pow(g, 2) - Math.Pow(((1 - Math.Pow(g, 2)) / (1 + g * (2 * randomNumber - 1))), 2));
newAngle += angle + cos;
while (newAngle >= 2 * Math.PI)
{
newAngle -= 2 * Math.PI; //normalize the angle to 0 <= angle < 2PI
}
return newAngle;
}
private double calculateTauP()
{
Random r = new Random();
double distance = -1 * Math.Log(1 - r.NextDouble());
return distance;
}
private double calculateTau(double angle, double tauP)
{
double tau = tauP * Math.Cos(Math.PI/2 - angle);
return tau;
}
}
}
Stop using Invoke and BeginInvoke to update the UI. Despite what you may have been told it is not that great of solution. Actually, in situations like these where all you want to to do is update the UI with progress information it is probably the worst solution. Instead, have your worker thread publish its progress information to an immutable data structure that can be shared with the UI thread. Then have your UI thread poll for it on a reasonable interval using a System.Windows.Forms.Timer.
public class YourForm : Form
{
private class ProgressInfo
{
public ProgressInfo(ulong top, ulong direct, ulong diffuse, ulong absorbed, ulong failed, ulong photons)
{
// Set properties here.
}
public ulong Top { get; private set; }
public ulong Direct { get; private set; }
public ulong Diffuse { get; private set; }
public ulong Dbsorbed { get; private set; }
public ulong Failed { get; private set; }
public ulong Photons { get; private set; }
}
private volatile ProgressInfo progress = null;
private void calculateModel()
{
for (ulong photonCount = 0; photonCount < photons; photonCount++)
{
// Do your calculations here.
// Publish new progress information.
progress = new ProgressInfo(/* ... */);
}
}
private void UpdateTimer_Tick(object sender, EventArgs args)
{
// Get a local reference to the data structure.
// This is all that is needed since ProgressInfo is immutable
// and the member was marked as volatile.
ProgressInfo local = progress;
this.textBoxAlbedo.Text = ((double)local.Top / (double)local.Photons).ToString();
this.textBoxDirect.Text = ((double)local.Direct / (double)local.Photons).ToString();
this.textBoxDiffuse.Text = ((double)local.Diffuse / (double)local.Photons).ToString();
this.textBox1.Text = local.Absorbed.ToString();
this.textBox2.Text = local.Failed.ToString();
}
Notice several things here.
The code is a lot easier to understand and follow.
The UI thread gets to decide when and how often its controls should be updated.
You get more throughput on the worker thread since it does not have to wait for a response from the UI as would occur with Invoke.
I rip on these Invoke and BeginInvoke solutions a lot because in many cases they are terrible solutions. Using a BackgroundWorker is a little better, but it still forces you into the push method of updating the UI (via the same marshaling techniques behind the scenes nonetheless). The pull method can be (and often is) a more elegant solution and usually more efficient.
How can i execute the a particluar loop for specified time
Timeinsecond = 600
int time = 0;
while (Timeinsecond > time)
{
// do something here
}
How can i set the time varaible here, if i can use the Timer object start and stop method it doesnot return me time in seconds
Regards
NewDev
May be the following will help:
Stopwatch s = new Stopwatch();
s.Start();
while (s.Elapsed < TimeSpan.FromSeconds(600))
{
//
}
s.Stop();
If you want ease of use:
If you don't have strong accuracy requirements (true millisecond level accuracy - such as writing a high frames per second video game, or similar real-time simulation), then you can simply use the System.DateTime structure:
// Could use DateTime.Now, but we don't care about time zones - just elapsed time
// Also, UtcNow has slightly better performance
var startTime = DateTime.UtcNow;
while(DateTime.UtcNow - startTime < TimeSpan.FromMinutes(10))
{
// Execute your loop here...
}
Change TimeSpan.FromMinutes to be whatever period of time you require, seconds, minutes, etc.
In the case of calling something like a web service, displaying something to the user for a short amount of time, or checking files on disk, I'd use this exclusively.
If you want higher accuracy:
look to the Stopwatch class, and check the Elapsed member. It is slightly harder to use, because you have to start it, and it has some bugs which will cause it to sometimes go negative, but it is useful if you need true millisecond-level accuracy.
To use it:
var stopwatch = new Stopwatch();
stopwatch.Start();
while(stopwatch.Elapsed < TimeSpan.FromSeconds(5))
{
// Execute your loop here...
}
Create a function for starting, stopping, and elapsed time as follows:
Class CustomTimer
{
private DateTime startTime;
private DateTime stopTime;
private bool running = false;
public void Start()
{
this.startTime = DateTime.Now;
this.running = true;
}
public void Stop()
{
this.stopTime = DateTime.Now;
this.running = false;
}
//this will return time elapsed in seconds
public double GetElapsedTimeSecs()
{
TimeSpan interval;
if (running)
interval = DateTime.Now - startTime;
else
interval = stopTime - startTime;
return interval.TotalSeconds;
}
}
Now within your foreach loop do the following:
CustomTimer ct = new CustomTimer();
ct.Start();
// put your code here
ct.Stop();
//timeinsecond variable will be set to time seconds for your execution.
double timeinseconds=ct.GetElapsedTime();
use Timers in c#
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
It's ugly .... but you could try this:
DateTime currentTime = DateTime.Now;
DateTime future = currentTime.AddSeconds(5);
while (future > currentTime)
{
// Do something here ....
currentTime = DateTime.Now;
// future = currentTime.AddSeconds(5);
}
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Accord.Video.FFMPEG;
namespace TimerScratchPad
{
public partial class Form1 : Form
{
VideoFileWriter writer = new VideoFileWriter();
int second = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
writer.VideoCodec = VideoCodec.H264;
writer.Width = Screen.PrimaryScreen.Bounds.Width;
writer.Height = Screen.PrimaryScreen.Bounds.Height;
writer.BitRate = 1000000;
writer.Open("D:/DemoVideo.mp4");
RecordTimer.Interval = 40;
RecordTimer.Start();
}
private void RecordTimer_Tick(object sender, EventArgs e)
{
Rectangle bounds = Screen.GetBounds(Point.Empty);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
writer.WriteVideoFrame(bitmap);
}
textBox1.Text = RecordTimer.ToString();
second ++;
if(second > 1500)
{
RecordTimer.Stop();
RecordTimer.Dispose();
writer.Close();
writer.Dispose();
}
}
}
}
Instead of such an expensive operation I'd recommend this: It's nasty but it's better to sit than running for doing nothing heating the cpu unnecesarily, the question is may be academic.
using System.Threading;
Thread.Sleep(600000);
I am doing this lab out of a book on my own, and I made an application in which sharks are racing. There is a radio button that should update a label on the right dynamically, as well as a button that actually starts the race. Everything used to work and then I renamed a few things, and now nothing works.
Screenshot of application:
image http://cl.ly/f08f4e22761464e0c2f3/content
Form Class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace project1
{
public partial class Game : Form
{
private Shark[] sharks;
private Guy[] guys;
private Guy selectedGuy;
public Game()
{
InitializeComponent();
Random moreRandom = new Random();
int start = myTrack.Location.X;
int finish = myTrack.Width - 65;
sharks = new Shark[4]
{
new Shark() {myRandom = moreRandom, myPictureBox = myShark1, myPBStart = start, trackLength = finish},
new Shark() {myRandom = moreRandom, myPictureBox = myShark2, myPBStart = start, trackLength = finish},
new Shark() {myRandom = moreRandom, myPictureBox = myShark3, myPBStart = start, trackLength = finish},
new Shark() {myRandom = moreRandom, myPictureBox = myShark4, myPBStart = start, trackLength = finish}
};
guys = new Guy[3]
{
new Guy() {myName="Joe", cash=50, myRadioButton=rbGuy1, myLabel=labelBet1},
new Guy() {myName="Bob", cash=75, myRadioButton=rbGuy2, myLabel=labelBet2},
new Guy() {myName="Al", cash=45, myRadioButton=rbGuy3, myLabel=labelBet3}
};
selectedGuy = guys[0];
rbGuy1.Tag = guys[0];
rbGuy2.Tag = guys[1];
rbGuy3.Tag = guys[2];
updateGui();
}
private void myChanged(object sender, EventArgs e)
{
selectedGuy = getSelectedGuy(sender);
betterLabel.Text = selectedGuy.myName;
}
private void betAmountValue(object sender, EventArgs e)
{
updateMin();
}
private void Bet_Click(object sender, EventArgs e)
{
int bet = (int) betAmount.Value;
int myFish = (int) sharkNumber.Value;
selectedGuy.placeBet(bet, myFish);
updateGui();
}
private void raceBtn_Click(object sender, EventArgs e)
{
betBtn.Enabled = false;
bool noWinner = true;
while(noWinner)
{
for (int dogFish = 0; dogFish < sharks.Length; dogFish++)
{
Application.DoEvents();
if(sharks[dogFish].Swim())
{
showWinner(dogFish);
collectBets(dogFish);
noWinner = false;
}
}
}
updateGui();
betBtn.Enabled = true;
}
private void showWinner(int fish)
{
MessageBox.Show(string.Format("Winner Winner People Dinner! \nShark {0} won!", fish + 1));
}
private void collectBets(int fish)
{
for (int guyNumber = 0; guyNumber < guys.Length; guyNumber++)
{
guys[guyNumber].collect(fish + 1);
guys[guyNumber].resetBet();
}
}
private void updateMin()
{
minBetLabel.Text = string.Format("Minimum bet: 5 bucks", betAmount.Value);
}
private Guy getSelectedGuy(object sender)
{
RadioButton rb = (RadioButton)sender;
return (Guy)rb.Tag;
}
private void updateGui()
{
for (int guyNumber = 0; guyNumber < guys.Length; guyNumber++)
{
guys[guyNumber].updateLabels();
}
for (int fish = 0; fish < sharks.Length; fish++)
{
sharks[fish].startPosition();
}
updateMin();
}
}
}
Shark Class:
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace project1
{
public class Shark
{
public int myPBStart; // Where the PictureBox starts
public int trackLength; // How long the racetrack is
public PictureBox myPictureBox = null; // The PictureBox object
public int location = 0; // My location on the racetrack
public Random myRandom; // An instance of Random
public Shark()
{
location = 0;
myPictureBox = new PictureBox();
myRandom = new Random();
trackLength = 100;
myPBStart = 0;
}
public bool Swim()
{
int distance = myRandom.Next(1, 4);
location += distance;
movePB(distance);
return location > trackLength;
}
private void movePB(int distance)
{
Point p = myPictureBox.Location;
p.X += distance;
myPictureBox.Location = p;
}
public void startPosition()
{
location = myPBStart;
Point p = myPictureBox.Location;
p.X = location;
myPictureBox.Location = p;
}
}
}
I can supply more resources if needed, but this is the main gist of it.
Using Visual Studio, make sure of the following:
1) For each radio button, verify the CheckedChanged event is hooked up to your myChanged function.
2) Verify the "Bets" Button.Click event is hooked up to your Bet_Click function.
3) Verify the "Race!" Button.Click event is hooked up to your raceBtn_Click function.
A safe way to rename things is to right click on the variable name, Refactor, Rename. This will ensure any references to the variable are renamed properly
when you renamed them you probably did it by editing the code rather than by changing the control properties.
THe Winforms designer in VS created code for you behind the scenes that wires the invents up. This codes uses the control names. Look for a file called formname_designer.cs. Notice that there are lines that still have the old control names. You can change this code
This is why its a good habit to give controls nice names when you start.
Make sure that the events on your controls are still connected to the correct event handlers in your code. Sometimes when you rename things, this link can get broken.