Get date and time inside ElapsedEventHandler - c#

So every 5 seconds the myTimer2 function gets executed. I'm trying to display the date and time when the function gets executed. Unfortunately, I'm getting System.InvalidOperationException error
"Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on"
public UpdateForm2()
{
InitializeComponent();
Timer x = new Timer(5000);
x.AutoReset = true;
x.Elapsed += new System.Timers.ElapsedEventHandler(myTimer2);
x.Start();
}
public void myTimer2(object sender, System.Timers.ElapsedEventArgs e)
{
// Getting error on this line.
textBox1.Text = "The textbox has been updated on " + DateTime.Now.ToString("HH:mm:ss tt") + Environment.NewLine;
}

You can use invoke.
public UpdateForm2()
{
InitializeComponent();
Timer x = new Timer(5000);
x.AutoReset = true;
x.Elapsed += new System.Timers.ElapsedEventHandler(myTimer2);
x.Start();
}
public void myTimer2(object sender, System.Timers.ElapsedEventArgs e)
{
this.Invoke(new MethodInvoker(delegate {
textBox1.Text = "The textbox has been updated on " + DateTime.Now.ToString("HH:mm:ss tt") + Environment.NewLine;
}));
}

Related

UI unresponsive + progress bar not working while using backgroundworker c#

I'm working on a WPF application in Visual Studio, I need to download a large file and extract it in my code. Someone recommended that I use background workers, but now when I try to increase the value on my progress bar it doesn't work... Can anyone help?
public void InstallVersion(string version)
{
string location = File.ReadAllText(#"C:\Users\" + Environment.UserName + #"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
location = location + #"\Versions\" + version;
if (File.Exists(location + ".zip"))
File.Delete(location + ".zip");
if (Directory.Exists(location))
{
DirectoryInfo di = new DirectoryInfo(location);
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in di.GetDirectories())
{
dir.Delete(true);
}
}
if (!myWorker.IsBusy)
{
myWorker.RunWorkerAsync();
}
}
And here is my worker code
public MainWindow()
{
InitializeComponent();
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
myWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
myWorker.ProgressChanged += new ProgressChangedEventHandler(myWorker_ProgressChanged);
myWorker.WorkerReportsProgress = true;
myWorker.WorkerSupportsCancellation = true;
}
protected void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
string location = File.ReadAllText(#"C:\Users\" + Environment.UserName + #"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
location = location + #"\Versions\" + Version;
WebClient Client = new WebClient();
string url = "";
string content = "";
string downloadlink = "";
List<string> availibleVersions = new List<string>();
List<string> versionDownload = new List<string>();
url = "https://midnightfalls.glitch.me/versions.html";
content = Client.DownloadString(url);
foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
{
if (line.Contains("0"))
{
availibleVersions.Add(line);
}
}
url = "https://midnightfalls.glitch.me/versionslink.html";
content = Client.DownloadString(url);
foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
{
if (line.Contains("https"))
{
versionDownload.Add(line);
}
}
for (var i = 0; i < availibleVersions.Count; i++)
{
if (availibleVersions[i] == Version)
{
downloadlink = versionDownload[i];
}
}
Client.DownloadFile(downloadlink, location + ".zip");
ZipFile.ExtractToDirectory(location + ".zip", location);
File.Delete(location + ".zip");
RunGame(Version);
}
protected void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
protected void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
Also, while my worker is running, the UI freezes... I'm pretty sure that's not meant to happen.
EDIT:
The UI is now updating but the progress bar still doesnt work...
It looks like the problem is that the main thread is constantly working while the background worker is doing its work:
while (this.myWorker.IsBusy)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
means that your main thread is constantly doing stuff while the background job is working, which is why the UI doesn't update.
You need to move the progress update to the background worker (where you could also set a value that actually makes some sense, e.g. to indicate how many of the 'availableVersion's you have downloaded).
Hope that makes sense.
EDIT:
Suppose we'll put all the code directly in the view, so assume we have a progress bar named 'progressBar' and a button named 'btnStart' (which kicks of the backgroundworker).
Here's the codebehind:
private BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
this.worker = new BackgroundWorker();
this.worker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
this.worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
this.worker.ProgressChanged += new
ProgressChangedEventHandler(myWorker_ProgressChanged);
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
}
private void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar.Value = e.ProgressPercentage;
}
private void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Whatever you need to do when finished here (alert, update a label, etc.)
}
private void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Just loop and report new progress. Sleep a little in between each
// progress update so that it isn't over before we have a chance to see it.
for(int i=0;i<100;i++)
{
Thread.Sleep(200);
this.worker.ReportProgress(i);
}
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
this.worker.RunWorkerAsync();
}
What happens is that the background worker fires an event notifying that progress has changed.
The main thread has a handle on that event, and updates the progress bar.
Since it's a background worker you don't need to use Dispatcher.Invoke - this is already taken care of.
Hope the example clarifies things for you.
I'm pretty sure you're blocking right here:
while (this.myWorker.IsBusy)
{
this.Dispatcher.Invoke(() =>
{
progress.Value += 10;
});
}
You should be calling ReportProgress on your BackgroundWorker instance within your myWorker_DoWork method.
Also, if you're using .NET 4.5 or later, you can dump the BackgroundWorker entirely and rewrite this code using the async/await pattern.

Beginner Thread or Dispatcher direction

I was hoping someone could point me in the right direction. I want to make a simple WPF application that has a button and a textbox. I click the button, and it starts to loop downloading a bunch of files. I can't seem to figure out how to not let the downloading stop the UI from updating. From what I can gather I'm probably going to have to use some threading code; but so far all the examples I've found and tried don't work for me. Any help or direction on where I should look and learn would be great. I can't seem to figure out how I can output those textbox.text messages around each file download.
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
tbOutput.Text += "Starting Download of : " + ticker + "\n";
webClient.DownloadFile(url, csvfile);
tbOutput.Text += "End Download of : " + ticker + "\n";
numStocks++;
}
tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";
If you mark your method as async, you can use the DownloadFileTaskAsync method
await webClient.DownloadFileTaskAsync(url, csvfile)
If you choose to use the BackgroundWorker, it allows you to output those messages into the TextBox around each file download. Here is a crude example adapted for your requirement.
1) At the class level, create an instance of the BackgroundWorker class and add event handlers to the BackgroundWorker instance's events:
BackgroundWorker workerDownload = new BackgroundWorker();
workerDownload.WorkerReportsProgress = true;
workerDownload.DoWork += workerDownload_DoWork;
workerDownload.ProgressChanged += workerDownload_ProgressChanged;
workerDownload.RunWorkerCompleted += workerDownload_RunWorkerCompleted;
2) Create an event handler for the background worker's DoWork event:
The DoWork event handler is where you run the time-consuming operation
on the background thread. Any values that are passed to the background
operation are passed in the Argument property of the DoWorkEventArgs
object that is passed to the event handler.
private void workerDownload_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var ticker in tickers)
{
// you can pass the required info as argument:
string[] arrArg = (string[])e.Argument;
string theUrl = arrArg[0];
string directory = arrArg[1];
var url = string.Format(theUrl, ticker);
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
// perform the download operation and report progress:
workerDownload.ReportProgress(0, "Starting Download of : " + ticker + "\n");
webClient.DownloadFile(url, csvfile);
workerDownload.ReportProgress(100, "End Download of : " + ticker + "\n");
numStocks++;
}
}
3) Create an event handler for the background worker's ProgressChanged event:
In the ProgressChanged event handler, add code to indicate the
progress, such as updating the user interface.
private void workerDownload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
tbOutput.Text += e.UserState.ToString();
}
4) Create an event handler for the RunWorkerCompleted event:
The RunWorkerCompleted event is raised when the background worker has
completed.
private void workerDownload_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
tbOutput.Text += "Total stocks downloaded = " + numStocks + "\n";
}
5) Start running the background operation by calling the RunWorkerAsync method:
int numStocks = 0;
string strDirectory = "<a_directory>";
string strUrl = string.Format(urlPrototype, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
string[] args = new string[2] { strUrl, strDirectory };
workerDownload.RunWorkerAsync(args);
There are a lot ways to implement it. For example:
1) Using async/await if you programming in .Net Framework 4.5. It is simpler than BackgroundWorker
https://msdn.microsoft.com/en-us/library/hh191443.aspx
private async void button_Click(object sender, RoutedEventArgs e)
{
Uri someUrl=new Uri(#"http://dotnetperls.com");
WebClient webClient=new WebClient();
await webClient.DownloadFileTaskAsync(someUrl, csvFile);
}
2) BackgroundWorker. This class is really intended to make asynchronous operations to avoid freezing UI.
See http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker
public partial class MainWindow : Window
{
BackgroundWorker bw;
public MainWindow()
{
InitializeComponent();
bw = new BackgroundWorker();
bw.DoWork += bw_DoWok;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
}
}
void bw_RunWorkerComleted(object sender, RunWorkerCompletedEventAgs e)
{
MessageBox.Show("The result is " + e.Result.ToString());
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
webClient.DownloadFile(url, csvfile);
numStocks++;
}
e.Result = "End Of Download ";
}
private void button_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
tbOutput.Text += "Starting Download of : " + ticker + "\n";
}
3) Use Thread class and update using Dispatcher class:
ThreadStart job = new ThreadStart(() =>
{
foreach (var ticker in tickers)
{
var url = string.Format(urlPrototype, ticker, startMonth, startDay, startYear, finishMonth, finishDay, finishYear, "d");
var csvfile = directory + "\\" + ticker.ToUpper() + ".csv";
webClient.DownloadFile(url, csvfile);
numStocks++;
}
Dispatcher.BeginInvoke((Action)(()=> tbOutput.Text += "End Download of : " + ticker + "\n";}));
});
Thread thread = new Thread(job);
thread.Start();
http://www.beingdeveloper.com/use-dispatcher-in-wpf-to-build-responsive-applications

Automatically send text every 10 seconds

I have a program that gets the app name, place them on a listbox and sends them to the other window if you click the send button.
What I wanted to know is, is it possible for it to automatically send every 10 seconds after a single click on the send button? If yes, how can I possibly do that?
There's the codes, in case if it brings of any help.
private void cmd_send_Click_1(object sender, EventArgs e)
{
String processID = "";
String processName = "";
String processFileName = "";
String processPath = "";
string hostName = System.Net.Dns.GetHostName();
listBox1.BeginUpdate();
try
{
for (int i = 0; i < listBox1.Items.Count; i++)
{
piis = GetAllProcessInfos();
try
{
// String pno = textBox4.Text.ToString();
// String path = textBox5.Text.ToString();
// String name = textBox6.Text.ToString();
// String user = textBox7.Text.ToString();
// output.Text += "\n Sent data : " + pno + " " + user + " " + name + " " + path ;
processID = piis[i].Id.ToString();
processName = piis[i].Name.ToString();
processFileName = piis[i].FileName.ToString();
processPath = piis[i].Path.ToString();
output.Text += "\n\nSENT DATA : \n\t" + processID + "\n\t" + processName + "\n\t" + processFileName + "\n\t" + processPath + "\n";
}
catch (Exception ex)
{
wait.Abort();
output.Text += "Error..... " + ex.StackTrace;
}
NetworkStream ns = tcpclnt.GetStream();
String data = "";
//data = "--++" + " " + textBox4.Text + " " + textBox5.Text + " " + textBox6.Text + " " + textBox7.Text;
data = "--++" + " " + processID + " " + processPath + " " + processFileName + " " + hostName;
if (ns.CanWrite)
{
byte[] bf = new ASCIIEncoding().GetBytes(data);
ns.Write(bf, 0, bf.Length);
ns.Flush();
}
}
}
finally
{
listBox1.EndUpdate();
}
}
Any help would be greatly appreciated.
You could place your code inside a single method, call that method initially on button click and start/stop your timer depending on it's current state.
private Timer _timer;
public Form() // Initialize timer in your form constructor
{
InitializeComponent();
_timer = new Timer();
_timer.Interval = 10000; // miliseconds
_timer.Tick += _timer_Tick; // Subscribe timer to it's tick event
}
private void _timer_Tick(object sender, EventArgs e)
{
SendData();
}
private void cmd_send_Click_1(object sender, EventArgs e)
{
if (!_timer.Enabled) // If timer is not running send data and start refresh interval
{
SendData();
_timer.Enabled = true;
}
else // Stop timer to prevent further refreshing
{
_timer.Enabled = false;
}
}
private void SendData()
{
// Your code here
}
EDIT:
If you're using .NET framework 4.5 or above you can do the same thing in using async/await.
private bool keepRefreshing;
private async void cmd_send_Click_1(object sender, EventArgs e)
{
if (keepRefreshing)
{
keepRefreshing = false;
return;
}
keepRefreshing = true;
while (keepRefreshing)
{
// Your code here
await Task.Delay(10000);
}
}
On button click it will send data and it will keep sending with delay of 10 seconds. When you press the button second time it will stop refreshing interval, third time it will start again and so on..
// Declare a timer
Timer tmr = new Timer();
tmr.Interval = 10000; // 10 second
tmr.Tick += timerHandler; // We'll write it in a bit
tmr.Start(); // The countdown is launched!
private void timerHandler(object sender, EventArgs e) {
// Here the code what you need each 10 seconds
tmr.Stop(); // Manually stop timer, or let run indefinitely
}
Their are many ways one is follow.
private void cmd_send_Click_1(object sender, EventArgs e)
{
bool isResend=true;
while (isResend==true)
{
// Put all your here
System.Threading.Thread.Sleep(10000);
}
}
Other ways are using Timer, etc...
Everyone's answer is cool, but as for me if you really need that "click" as start, i'll do it this way.
Initiate events for timer & background worker inside form load.
set timer.start(); inside click.
Once ticking, if backgroundworker is not busy, execute background worker.
Ensure that you don't directly set label1.text = "send some works here." inside the background worker, it will cause error.
Hope this helps.

Creating a statusfield for users

I need some guidance here on why this isn't working:
So here's the issue, I want to give my users a little status field so they can check how long it will take and get a coffee or two for them.
My Problem is that the statusfield (2 Labels), are not updated during the process.
This is my current code :
private void Cancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void start_change_Click(object sender, EventArgs e)
{
DialogResult dr = MessageBox.Show("Start process?", "DateChanger", MessageBoxButtons.OKCancel, MessageBoxIcon.Hand);
if (dr == DialogResult.OK)
{
//get files
List<String> d = new List<String>();
label_status_title.Text = "Status: collecting Data, take a coffee while waiting.\nfiles changed: 0 files";
d = getFiles("H:\\");
int i = 0;
double diff = 0.0;
//modify files
label_status_title.Text = "Status: changing files.\nfiles changed: 0/" + d.Count + " files.";
foreach (String s in d)
{
String label = "\nfile: " + s;
//create newDate and modify creation and lastwrite
DateTime actualDate = Directory.GetLastWriteTime(s).Date;
DateTime newDate = new DateTime(2015, 03, 01);
diff = (newDate - actualDate).TotalDays;
label += "\nactual creation date: " + Directory.GetCreationTime(s).Date;
label += "\nnew creation date: " + newDate.Date;
label += "\nactual last write date: " + Directory.GetLastWriteTime(s).Date;
label += "\nnew last write date: " + newDate.Date;
if (diff > 400)
{
try
{
//set new timevalues
Directory.SetCreationTime(s, newDate);
Directory.SetCreationTimeUtc(s, newDate);
Directory.SetLastWriteTime(s, newDate);
Directory.SetLastWriteTimeUtc(s, newDate);
}
catch (UnauthorizedAccessException UAE)
{
}
i++;
label += "\nchange needed.";
}
else
{
label += "\nchange not needed.";
}
label_status.Text = label;
label_status_title.Text = "Status: changing files.\nfiles changed: " + i + "/" + d.Count + " files.";
}
MessageBox.Show("Process finished, changed: " + i + "/" + d.Count + " files.");
}
}
private List<String> getFiles(string sDir)
{
List<String> files = new List<String>();
try
{
foreach (string f in Directory.GetFiles(sDir))
{
files.Add(f);
}
foreach (string d in Directory.GetDirectories(sDir))
{
files.AddRange(getFiles(d));
}
}
catch (System.Exception excpt)
{
MessageBox.Show(excpt.Message);
}
return files;
}
private void DateChanger_Load(object sender, EventArgs e)
{
String label = "";
label_status_title.Text = "Status: \nfiles changed: 0 files";
label += "file: ";
label += "\nactual creation date: ";
label += "\nnew creation date: ";
label += "\nactual last write date: ";
label += "\nnew crealast writetion date: ";
label_status.Text = label;
}
I also tried the suggestion of using MethodInvoker, but that also didn't work either. Any guidance or suggestions here are appreciated.
Thanks.
Mirko
p.s. if there is a better solution than using labels or text boxes for this feel free to tell me. :)
Youre Method start_change_Click(object sender, EventArgs e) is blocking the main thread. To avoid this, use a separate thread to update the labels.
Check out this post: Thread freezes main UI
Just refresh the Label after assigning it a new Text value.
label_status_title.Text = "Status: changing files.\nfiles changed: " + i + "/" + d.Count + " files.";
label_status_title.Refresh(); //added

C# InvalidOperationException AND Cross-thread operation [duplicate]

This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 4 years ago.
In my windows form I have a text box and a button, the text box “tb_LogBox” is multiline text box I am trying to create a background worker that is supposed to call a function i.e. “LogTimer.DnT()” when I compile is and run it Visual studio throws InvalidOperationException.
The actual Error I am getting
Cross-thread operation not valid: Control 'tb_LogBox' accessed from a thread other than the thread it was created on. The following sample code illustrate what I am trying to do
private void button1_Click(object sender, EventArgs e)
{
try
{
var bw = new BackgroundWorker();
bw.DoWork += ExecuteOperations ;
bw.RunWorkerAsync();
}
catch (Exception ex)
{
tb_LogBox.AppendText(Environment.NewLine + " =#= " + ex.Message+" "+ex.Source);
}
}
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on
}
public class LogTimer
{
public string DnT()
{
const string datePat = #"d/MM/yyyy";
var dateTime = DateTime.Now();
return dateTime.ToString(datePat);
}
}
Try to use the begin invoke method :
BeginInvoke(new Action(() =>
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT());
}));
This would be smoother than Invoke.
you need to marshall the Ui change onto the UI thread. This can be performed by using an invoke/begininvoke call around your tb_LogBox.AppendText
in a Winforms Application:
this.BeginInvoke((MethodInvoker)delegate
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
in a WPF application:
this.Dispatcher.BeginInvoke(
(Action)delegate()
{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
Hope this helps!
Do this in your ExecuteOperations:
tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) }));
You cannot use other threads (BackgroundWorker uses a .NET threadpool thread) to make changes to UI components. This is a major hurdle you will have to get used to in WinForms programming.
The BackgroundWorker is executing on its own thread and all operations related to WinForms GUI elements must run on the thread they were created on. They way you currently use the BackgroundWorker is identical to just Queuing the operation using ThreadPool.QueueUserWorkItem(). For communication back to the GUI using a BackgroundWorker, use ReportProgess or set the DoWorkEventArgs.Result property in the worker method, and react to the corresponding events on the GUI thread. You can also use Invoke/BeginInvoke on a WinForms control to execute arbitrary code directly on the GUI thread. In your case that would mean replacing the line accessing the tb_LogBox with:
tb_LogBox.Invoke(new Action(() =>
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
));
You need to invoke the control's method on the UI thread:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
tb_LogBox.Invoke((MethodInvoker)delegate{
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
I don't know what LogTimer does, but it may very well be that you should create that inside the delegate as well:
private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
tb_LogBox.Invoke((MethodInvoker)delegate{
var FuncCall = new LogTimer();
tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
});
}
You can't access the host thread from the background worker's execution thread. You can use the ReportProgress method of the BackgroundWorker to send information to the host thread.
private void button1_Click(object sender, EventArgs e)
{
try
{
var bw = new BackgroundWorker();
bw.DoWork += ExecuteOperations;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync();
}
catch (Exception ex)
{
tb_LogBox.AppendText(Environment.NewLine + " =#= " + ex.Message + " " + ex.Source);
}
}
private static void ExecuteOperations(object sender, DoWorkEventArgs e)
{
var FuncCall = new LogTimer();
string text = Environment.NewLine + FuncCall.DnT();
(sender as BackgroundWorker).ReportProgress(0, text);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
tb_LogBox.AppendText(e.UserState as string);
}
public class LogTimer
{
public string DnT()
{
const string datePat = #"d/MM/yyyy";
var dateTime = DateTime.Now;
return dateTime.ToString(datePat);
}
}

Categories