Why doesn't System.Timers.Timer update non-busy UI? - c#

I have been looking for a reason, but about every single topic on stackoverflow I could find suggested a busy UI as the reason.
I have very basic code, just for testings and it simply won't update the UI, even though I am sure it cannot be busy doing something else:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
static double t = 0;
static double dt = 0.1;
private void button1_Click(object sender, RoutedEventArgs e)
{
Timer timer = new Timer(5000);
timer.Enabled = true;
timer.Elapsed += new ElapsedEventHandler(Update);
timer.Start();
}
void Update(object sender, ElapsedEventArgs e)
{
t = t + dt;
testBlock1.Text = (t).ToString();
}
}
I debugged it and set a breakpoint to the textBlock1.Text update and it did break every 5 seconds, but the UI is never updated. As can be seen in the code, when I move my mouse over the Button I use to start the timer, the button shows its typical "mouse-over-button" animation. Why does that happen, but not the text update?
If anyone can lead me to a good topic on stackoverflow, please do so and I will delete my question here, but I couldn't find any topic explaining why this approach doesn't update the UI even though the UI is not busy doing anyting else.

System.Timers.Timer won't by default marshal back to the UI thread. In Windows Forms you can make it do so easily enough:
Timer timer = new Timer(5000);
timer.SynchronizingObject = this;
... but WPF UI elements don't implement ISynchronizeInvoke, which would stop this from working for you in WPF.
You could use the Dispatcher to marshal over to the UI thread within the handler, or you could just use a DispatcherTimer to start with.

For WPF, you should use DispatcherTimer -
Also with the above code you posted, i am getting a cross thread error since elapsed event is raised on other thread and not on UI thread.
private void button1_Click(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 5);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
t = t + dt;
txt.Text = (t + dt).ToString();
}
EDIT
Also, you can marshal it on UI Dispatcher with your existing code like this -
void Update(object sender, ElapsedEventArgs e)
{
App.Current.Dispatcher.Invoke((Action)delegate()
{
t = t + dt;
txt.Text = (t + dt).ToString();
});
}

Related

How to setStatus after timer.Elapsed

My question is probably quite simple. In a c# project i am trying to set a status of an instance in a different class at a click event. The trouble is that i want to do this after a certain time has elapsed and without any c# experience I find this quite hard to accomplish.
Thanks in advance!!
my code is as follows:
public void button1_Click(object sender, EventArgs e)
{
kruispunt.zPad1.voetstoplicht1.setStatus(StoplichtStatus.Rood);
kruispunt.zPad1.voetstoplicht2.setStatus(StoplichtStatus.Rood);
this.Refresh();
}
The easiest way is to use async (assuming you're using C# 5):
public async void button1_Click(object sender, EventArgs e)
{
await Task.Delay(Timespan.FromSeconds(5));
kruispunt.zPad1.voetstoplicht1.setStatus(StoplichtStatus.Rood);
kruispunt.zPad1.voetstoplicht2.setStatus(StoplichtStatus.Rood);
this.Refresh();
}
Another option is to use a Timer:
public void button1_Click(object sender, EventArgs e)
{
var timer = new System.Windows.Forms.Timer { Interval = 5000 };
timer.Tick += delegate
{
timer.Dispose();
kruispunt.zPad1.voetstoplicht1.setStatus(StoplichtStatus.Rood);
kruispunt.zPad1.voetstoplicht2.setStatus(StoplichtStatus.Rood);
this.Refresh();
}
timer.Start();
}
Note that I used a Windows Forms timer, rather than System.Timers.Timer or System.Threading.Timer; this is because the event must occur in the UI thread, otherwise the call to Refresh would fail.

C# show timer in textbox

I'm trying to make a timer run in a textbox and I haven't had any luck.
This is the code I'm using:
private static System.Timers.Timer timer;
...
private void StartBtn_Click(object sender, EventArgs e)
{
timer = new System.Timers.Timer(1000);
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Enabled = true;
}
...
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
TimeTb.Text = e.SignalTime.ToString();
}
But nothing happens.
I tried this:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
MessageBox.Show(e.SignalTime.ToString(),
"Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
}
And it worked fine. Anyone know why it isn't working with my textbox?
The Elapsed Event runs on a different Thread then the UI. Its not allowed to manipulate UI Objects from a different thread and an Exception should surface in your EventHandler. Since you don't handle Exceptions there you won't notice it.
In StartBtn_Click set the SynchronizingObject Property of the Timer to this (the form). Then the elapsed Event will be synchronized with the main thread.
May be you should start the timer first.
Take a look at:
http://msdn.microsoft.com/en-us/library/system.timers.timer.start.aspx
So add:
timer.Start();
Put a try catch around OnTimedEvent and see if there are any issues with threading.
If there are, try using the System.Windows.Forms.Timer which can resolve the issues of cross threading.
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
As stated:
Implements a timer that raises an event at user-defined intervals.
This timer is optimized for use in Windows Forms applications and must
be used in a window.
Your OnTimedEvent callback will not be called on the UI thread, so you will get an exception in there trying to set the textbox text. So you need to change the event handler to look like this:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (TimeTb.InvokeRequired)
{
TimeTb.Invoke((MethodInvoker)delegate
{
OnTimedEvent(source, e);
});
}
TimeTb.Text = e.SignalTime.ToString();
}
Maybe using Application.DoEvents(); after TimeTb.Text = e.SignalTime.ToString(); would work, though using it would not be recommended (Use of Application.DoEvents()).
you could do something like
private void Button_Click(object sender, RoutedEventArgs e)
{
this._timer = new DispatcherTimer();
this._timer.Interval = TimeSpan.FromMilliseconds(1);
this._timer.Tick += new EventHandler(_timer_Tick);
_timer.Start();
}
void _timer_Tick(object sender, EventArgs e)
{
t= t.Add(TimeSpan.FromMilliseconds(1));
textBox.Text = t.Hours + ":" + t.Minutes + ":" + t.Seconds + ":" + t.Milliseconds;
}
Edit :
How to add Assembly: WindowsBase

C# Threading, 1st Task DoSomething, 2nd Task update a WinFormLabel (Realtime)?

Lets say I have Task 1:
private void Task1()
{
//Here is some Code, could be any "longer" Task -
//For Example: Grab all words from a .txt File and fill in a List<String>
}
Then I have an other Task 2:
private void Task2(string word)
{
//So lets say theres a Label on my WinForm..
//Now While Task1 is grabbing the words, Task2 should fill a Label
//with the added 'word' (parameter) - (Task2 will be called from Task1
}
Actually I don't know how to make this possible, or whats the best way. On the UI I should be able to see the Label.Text changing (every word).. So I need to make a second Thread? How could I do this? Maybe someone could help me, cheers
UPDATE:
I tried it now with the Backgroundworker, but something seems to be false.. its actually not working, nothing happens on the form
Code:
public void CreateAndSaveAMatch(DateTime date) //That method is being called several times
{
//HERE IS CODE, WHICH CREATES AND SAVES A MATCH
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(date);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(0, Convert.ToDateTime(e.Argument).ToShortDateString());
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = (string)e.UserState; //here on the Label I would like to show the Date
}
Ok, try this. This is a simple example that will show you how to solve your problem using BackgroundWorker. Also note that there are many other solutions. To use this example create a Form in a new project that only has a button and a label. Also note that this is a supplement of the other answers which were correct.
public partial class Form1 : Form
{
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.ProgressChanged += new ProgressChangedEventHandler(createAndSaveAMatchBGW_ProgressChanged);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
createAndSaveAMatchBGW.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("BackgroundWorker finished");
}
void createAndSaveAMatchBGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = ((DateTime)e.UserState).ToString("ss");
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker does something for a 10 seconds, each second it Reports
BackgroundWorker bgw = (BackgroundWorker)sender;
DateTime dt = (DateTime) e.Argument;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
dt = dt.AddSeconds(1);
bgw.ReportProgress(0, dt);
}
}
}
And if you report from CreateAndSave... method only once per its execution, then you can use this code:
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = ((DateTime)e.Result).ToString();
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
DateTime dt = (DateTime) e.Argument;
//you do something with your DateTime
dt = dt.AddDays(10);
e.Result = dt;
}
Use BackgroundWorker for reporting progress from first task. Drag this component from toolbox to your form, and subscribe to DoWork and ProgressChanged events. Also set property WorkerReportsProgress to true. Then start you first task asynchronously:
// this will execute code in `DoWork` event handler
backgroundWorker1.RunWorkerAsync();
Next - use userState object to pass processed words:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// grab words in a loop and report progress
backgroundWorker1.ReportProgress(0, word);
}
And last step - update label in ProgressChanged event handler
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text += (string)e.UserState; // this is your grabbed word
}
The simplest way to achieve this kind of thing is using BackgroundWorker.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
BackgroundWorker automatically handles thread marshalling and provides events that allow you to update the UI. The event handlers run on the UI thread.
The things you do in Task1 could be moved into a BackgroundWorker, and the updates to the UI that you propose to do in Task2 can actually be in response to progress events from BackgroundWorker.
ProgressChangedEventArgs provides for user-defined data that could hold the current word.
However, Winforms (and indeed pretty much any UI) will not be able to keep up with a separate CPU thread just loading words from a file if you intend to show every word you load.
Task1 could be started on a separate thread.
You wouldn't actually need a Task2 unless there was some complex logic being performed to update the TextBox. You you really need to do is use TextBox.Invoke() to invoke the update on the UI Thread from Task1.

GUI not updating until end of event

I have a gui that needs to be updated from a hardware device attached through a dll file and a textbox. My problem is that gui is not updated until the end of the event and I need to show something pause and then show something else. The hack of Application.DoWork didn't change anything. Anyone have any suggestions? Everything I was reading used either invoke or DoEvents and neither seem to change the behavior.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
App.myMainWindow.image1.Visibility = Visibility.Hidden;
System.Windows.Forms.Application.DoEvents();
}
Thread.Sleep(4000);
}
You make the GUI thread sleep, obviously the GUI cannot be updated when its thread sleeps. Create a seperate thread and use the Dispatcher to update UI-elements if you must, you can savely send that thread to sleep and your GUI will still respond.
Edit: System.Windows.Forms.Application.DoEvents(); you are sure about that WPF tag, aren't you?
Take a look at this web page for beginning http://www.c-sharpcorner.com/UploadFile/jieying/UsingProgressBarStatusBarandTimerControlsinVS.NET11282005021220AM/UsingProgressBarStatusBarandTimerControlsinVS.NET.aspx
I'd use a backgroundworker and start it in the click handler.
This way the GUI will continue to be available and progress can be displayed.
I figured it out using timers as vittore had suggested. This must be the way to do it since you cannot sleep the GUI thread during GUI event handlers.
public partial class MainWindow : Window
{
static Timer _timer;
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
this.image1.Visibility = Visibility.Hidden;
this.image2.Visibility = Visibility.Visible;
_timer = new Timer(2000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true; // Enable it
}
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
textBox1.Dispatcher.Invoke(new Action(delegate()
{
_timer.Enabled = false;
this.image1.Visibility = Visibility.Visible;
this.image2.Visibility = Visibility.Hidden;
}));
}

Opening A WinForm Programmatically In Another WinForm's Code

Happy Friday SO!
I'm building a multi-WinForm application and am having some troubles.
I have a main WinForm that stays open at all times. It hits a database every minute looking for changes, and if there is a change, it will open the second WinForm (this may seem like a stupid way to do things, but for my purpose, this is the best method).
Just sticking the following code into my Form1.cs doesn't do the trick:
Application.Run(new Form2());
Can you guys point me in the right direction? I have no idea where to turn.
Form2 form2 = new Form2();
form2.Show();
and to prevent a ton of forms being opened, maybe:
Form2 form2 = new Form2();
form2.ShowDialog();
#Comment:
A BackgroundWorker is used to keep your current UI Thread responsive. It was not designed to keep multiple forms pumping happily along. Look into running your intensive code as a Background thread within a ThreadPool.
If what you wish is to launch a long process and to show the progress to the user, for example just like when you have a progress bar or something alike, you should use a BackgroundWorker to do the job. Here's a simple example:
public partial class ProgressForm : Form {
// Assuming you have put all required controls on design...
// Allowing some properties to be exposed for progress update...
public properties MaximumProgress {
set {
progressBar1.Maximum = value;
}
public properties OverallProgress {
set {
progressBar1.Value = value;
}
}
public partial class MainForm : Form {
private BackgroundWorker backgroundWorker1;
private ProgressForm _pf;
public MainForm() {
InitializeComponent();
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
}
// Assuming process starts on Button click.
private void button1_Click(object sender, EventArgs e) {
_pf = new ProgressForm();
_pf.MaximumProgress = number-of-elements-to-treat-returned-by-prevision-or-whatever-else;
// Launching the background workder thread.
backgroundWorker1.RunWorkerAsync(); // Triggering the DoWork event.
// Then showing the progress form.
_pf.ShowDialog();
}
private void backgroundWorker1_DoWork(object sender, EventArgs e) {
LaunchProcess();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
_pf.OverallProgress = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, EventArgs e) {
_pf.Close();
_pf.Dispose();
}
private void LaunchProcess() {
// Do some work here...
// Reporting progress somewhere within the processed task
backgroundWorker1.ReportProgress();
}
}
This is not a compileable code as its purpose is to illustrate the main idea.
Now, is this something alike you want to do?

Categories