I wanted to make a simple Countdown-Application with C# to show as an example.
For the very first and basic version I use a Label to display the current time left in seconds and a Button to start the countdown. The Button's Click-Event is implemented like this:
private void ButtonStart_Click(object sender, RoutedEventArgs e)
{
_time = 60;
while (_time > 0)
{
_time--;
this.labelTime.Content = _time + "s";
System.Threading.Thread.Sleep(1000);
}
}
Now when the user clicks the Button the time is actually counted down (as the application freezes (due to Sleep())) for the chosen amount of time but the Label's context is not refreshed.
Am I doing something generally wrong (when it comes to Threads) or is it just a problem with the UI?
Thank you for your answers!
I now use a System.Windows.Threading.DispatcherTimer to do as you told me. Everything works fine so this question is officially answered ;)
For those who are interested: Here is my code (the essential parts)
public partial class WindowCountdown : Window
{
private int _time;
private DispatcherTimer _countdownTimer;
public WindowCountdown()
{
InitializeComponent();
_countdownTimer = new DispatcherTimer();
_countdownTimer.Interval = new TimeSpan(0,0,1);
_countdownTimer.Tick += new EventHandler(CountdownTimerStep);
}
private void ButtonStart_Click(object sender, RoutedEventArgs e)
{
_time = 10;
_countdownTimer.Start();
}
private void CountdownTimerStep(object sender, EventArgs e)
{
if (_time > 0)
{
_time--;
this.labelTime.Content = _time + "s";
}
else
_countdownTimer.Stop();
}
}
Yes, event handlers should not block - they should return immediately.
You should implement this by a Timer, BackgroundWorker or Thread (in this order of preference).
What you are seeing is the effect of a long-running message blocking the windows message queue/pump - which you more commonly associate with the white application screen and "not responding". Basically, if your thread is sleeping, it isn't responding to messages like "paint yourself". You need to make your change and yield control to the pump.
There are various ways of doing this (ripper234 does a good job of listing them). The bad way you'll often see is:
{ // your count/sleep loop
// bad code - don't do this:
Application.DoEvents();
System.Threading.Thread.Sleep(1000);
}
I mention this only to highlight what not to do; this causes a lot of problems with "re-entrancy" and general code management. A better way is simply to use a Timer, or for more complex code, a BackgroundWorker. Something like:
using System;
using System.Windows.Forms;
class MyForm : Form {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
Timer timer;
MyForm() {
timer = new Timer();
count = 10;
timer.Interval = 1000;
timer.Tick += timer_Tick;
timer.Start();
}
protected override void Dispose(bool disposing) {
if (disposing) {
timer.Dispose();
}
base.Dispose(disposing);
}
int count;
void timer_Tick(object sender, EventArgs e) {
Text = "Wait for " + count + " seconds...";
count--;
if (count == 0)
{
timer.Stop();
}
}
}
Related
I am writing up a C# application using Winforms, and I need to collect some data based on my selection from a comboBox. I also have a start button that enables data collection, and a stop button that halts the data collection.
This is what I am capable of doing right now:
Measure all data from all channels by switching my selection on the comboBox (one channel at a time). The process goes like: select a channel- start button click - wait for data collection - stop button click
But here is what I want to do in code:
Select a channel - start button click - wait for data collection - stop button click
switch to next channel - start button click -wait for data collection - stop button click
................
repeat this process until done.
My question is: what should I adopt to achieve this?
I have been trying to use startButton.PerformClick( ) to enabling the buttons, however, I need to stop for a few seconds in between starting and stopping to wait for data collection.
You may ask why because this is very inefficient, but for some reason the DLL from the third party cannot collect data from all channels at the same time. So i have to manually switch my channel selection from the comboBox in order to collect all data at one go.
Please let me know if you have any suggestions.
private void timer_Tick(object sender, EventArgs e)
{
startButton.PerformClick();
}
private void timer2_Tick(object sender, EventArgs e)
{
stopButton.PerformClick();
}
private void checkAll_CheckedChanged(object sender, EventArgs e)
{
int i = 0;
while (i != 40) //there are a total of 40 channels
{
System.Windows.Forms.Timer mytimer = new System.Windows.Forms.Timer();
mytimer.Interval = 5000;
System.Windows.Forms.Timer mytimer2 = new System.Windows.Forms.Timer();
mytimer2.Interval = 7000;
mytimer.Start();
mytimer.Tick += new EventHandler(timer_Tick);
mytimer2.Start();
mytimer2.Tick += new EventHandler(timer2_Tick);
physicalChannelComboBox.SelectedIndex = i;
i++;
Thread.Sleep(5000);
}
}
Here is a simple routine shell that should help you. This uses a timer to poll if the data is complete (checking a boolean, which is currently set to true for testing). In your case you could probably just set the timers tick value to be the delay you want. If you have a way of knowing when the data is done being collected for the channel, that would be more preferable. This code runs standalone so you can test and configure it before starting to integrate it into your existing code. All it requires is a a listbox for viewing the logs.
public partial class DataCollectorForm : Form
{
private Timer timer = new Timer();
private int numberOfChannels = 40;
private int currentChannelNumber = 0;
private DateTime routineStartTime;
private DateTime routineStopTime;
private DateTime channelStartTime;
private DateTime channelStopTime;
public DataCollectorForm()
{
InitializeComponent();
timer.Interval = 250;
timer.Tick += Timer_Tick;
DataCollectionRoutineStart();
}
private void Timer_Tick(object sender, EventArgs e)
{
// Need to check if data collection for this channel is complete..
//
var isDoneCollectingData = true;
if (isDoneCollectingData)
{
ChannelDataCollectionStop();
currentChannelNumber++;
if (currentChannelNumber >= numberOfChannels)
{
DataCollectionRoutineComplete();
}
else
{
ChannelDataCollectionStart();
}
}
}
public void DataCollectionRoutineStart()
{
routineStartTime = DateTime.Now;
Log("Data Collection Routine Start");
currentChannelNumber = 0;
ChannelDataCollectionStart();
timer.Start();
}
private void ChannelDataCollectionStart()
{
channelStartTime = DateTime.Now;
Log("Data Collection Start : channel " + currentChannelNumber);
}
private void ChannelDataCollectionStop()
{
channelStopTime = DateTime.Now;
Log("Data Collection Stop : channel " + currentChannelNumber + " : elapsed " + (channelStopTime - channelStartTime));
}
private void DataCollectionRoutineComplete()
{
routineStopTime = DateTime.Now;
timer.Stop();
Log("Data Collection Routine Complete : elapsed " + (routineStopTime - routineStartTime));
}
private void Log(string msg)
{
loggingListBox.Items.Add(msg);
}
}
This is my requirement I have to use while loop inside timer, after launch application after click on button UI is locked not able to move and text is not diplaying at textbox too
below is the code
using System;
using System.Windows.Forms;
namespace WinScreenLocked
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int Number = 0;
private void timer1_Tick(object sender, EventArgs e)
{
while (true)
{
textBox1.Text = Number.ToString();
Number++;
}
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Start();
}
}
}
// Create a 30 min timer
timer = new System.Timers.Timer(1800000);
// Hook up the Elapsed event for the timer.
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// do stuff
}
with the usual caveats of: timer won't be hugely accurate and might need to GC.KeepAlive(timer)
See also: Why does a System.Timers.Timer survive GC but not System.Threading.Timer?
you can stop the thread to block the ui i.e using
System.Threading.Thread.Sleep(2000);
it takes miliseconds in above 2000 miliseconds is equal to 2 seconds.
Seeing as this is winforms you can use Application.DoEvents() to process the UI refresh.
See this : https://social.msdn.microsoft.com/Forums/en-US/b1b1969d-8a51-496c-9274-a0ac1708a8b4/what-does-applicationdoevents-actually-do?forum=csharplanguage
I am trying to write an interface for a motorised stage. What I am trying to do is to create a scan feature such that the motor will move a certain distance, stop and wait a specified time and then move the same distance again. It will repeat the process until it has reached the total length specified by the user. To do this I am trying to use a Timer class features as I still want the GUI to be active during the scan.
I've got some idea of how to code it but get stuck. It would go something like:
private void btnGo_Click(object sender, EventArgs e) //On click
{
int i = 0;
int stop = 15; //number of times I want the motor to stop
System.Timers.Timer bTimer; //initialise timer
bTimer = new System.Timers.Timer(waittime); //time I want the motor to wait
bTimer.Elapsed += PerformMove;
bTimer.Enabled = true;
if(i==stop){bTimer.stop()}
}
private void PerformMove(Object source, ElapsedEventArgs e) //event to move motor
{
//movemotor
i++;
}
Not being particularly familiar with C# or timers is undoubtedly the cause of my confusion. What's the best way to approach this problem? Any example code would be great.
If somebody could clarify what the lines
bTimer.Elapsed += PerformMove;
bTimer.Enabled = true;
actually do too that would also be of great use!
EDIT (sorry, didn't think this was a key part): The value of stop is defined upon the user click of the button from a text box within the GUI. i.e.
int stop = Convert.ToDouble(tbIntervalStops.Text); //grab integer from user input upon button click
This would be the correct solution without memory leak
private int i = 0;
private int stop = 15; //number of times I want the motor to stop
private Timer bTimer; //initialise timer -> Thats wrong: nothing is INITIALIZED here its just defined
private void btnGo_Click(object sender, EventArgs e) //On click
{
i = 0;
stop = Convert.ToInt32(tbIntervalStops.Text); //using int because double is a floating point number like 12.34 and for counting only full numbers will be needed
bTimer = new System.Timers.Timer(waittime); //time I want the motor to wait + Here the Timer is INITIALIZED
bTimer.Elapsed += PerformMove; //Adds the Eventhandler, What should be called when the time is over
bTimer.Enabled = true; //This starts the timer. It enables running like pressing the start button on a stopwatch
}
private void PerformMove(Object source, ElapsedEventArgs e) //event to move motor
{
//movemotor
i++;
if (i == stop) //if stop would be a double here we will have the danger to get not a true because of rounding problems
{
bTimer.Stop();
//now enable the Garbage Collector to remove the Timer instance
bTimer.Elapsed -= PerformMove; //This removes the Eventhandler
bTimer.Dispose(); //This frees all resources held by the Timer instance.
bTimer = null;
}
}
Alternatively, you could also derive from the System.Timers.Timer object and create a wrapper which has properties specific to the task. In which case, you would simply need to instantiate a MoveTimer and subscribe to it's OnPerformMoveEvent.
Update: Added OnMovesCompletedEvent
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimerExample
{
public class MoveTimer : System.Timers.Timer
{
public event EventHandler OnPerformMoveEvent = delegate { };
public event EventHandler OnMovesCompletedEvent = delegate { };
public MoveTimer()
{
Initialize(new TimeSpan(), 0);
}
public MoveTimer(TimeSpan wait, int moves)
{
this.Initialize(wait, moves);
}
private int _i;
private int _totalmoves;
public int Moves
{
get { return this._totalmoves; }
set { this._totalmoves = value; }
}
private TimeSpan _wait;
public TimeSpan Wait
{
get { return this._wait; }
set { this._wait = value; }
}
private System.Timers.Timer _timer;
private void Initialize(TimeSpan wait, int moves)
{
this._totalmoves = moves;
this._wait = wait;
this._timer = new System.Timers.Timer(wait.Milliseconds);
}
private void BindComponents()
{
this._timer.Elapsed += _timer_Elapsed;
}
private void UnBindComponents()
{
this._timer.Elapsed -= _timer_Elapsed;
}
public void StartTimer()
{
this._timer.Enabled = true;
}
public void StopTimer()
{
this._timer.Enabled = false;
}
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this._i++;
if (this.OnPerformMoveEvent != null)
this.OnPerformMoveEvent(this, EventArgs.Empty);
if (this._i == this._totalmoves)
{
this._timer.Stop();
this.UnBindComponents();
this.Dispose();
if (this.OnMovesCompletedEvent != null)
this.OnMovesCompletedEvent(this, EventArgs.Empty);
}
}
}
}
In regards to the user input where the number of moves or stops is provided as a string. I would handle this outside of the MoveTimer object. Validation should always be performed.
First determine that the value can be parsed into an integer. If not, throw an exception to let the user know that the input was entered incorrectly.
To use the above, something like the following would be all it requires:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TimerExample
{
class Program
{
static void Main(string[] args)
{
//Create move timer that will trigger 15 times, once every 30 seconds.
MoveTimer moveTimer = new MoveTimer(new TimeSpan(0, 0, 30), 15);
//Substribe to the move timer events
moveTimer.OnPerformMoveEvent += moveTimer_OnPerformMoveEvent;
moveTimer.OnMovesCompletedEvent += moveTimer_OnMovesCompletedEvent;
//Start the timer
moveTimer.StartTimer();
//What happens in between the moves performed?
}
static void moveTimer_OnMovesCompletedEvent(object sender, EventArgs e)
{
//All the moves have been performed, what would you like to happen? Eg. Beep or tell the user.
}
static void moveTimer_OnPerformMoveEvent(object sender, EventArgs e)
{
//Timer has lapsed, what needs to be done when a move is requested?
}
}
}
I am writing activity recognition software in realtime. But i can't setText in textbox with infinite loop. I try to search Google but no answer. When, i using "textbox.Text += "ZZZZ", it working but I using "textbox.Text = "ZZZ" ", it not working. I hope someone can point me how to solve
private void button1_Click(object sender, EventArgs e)
{
for (; ; ){
Thread.Sleep(20);
........process....
tb_activity = "AAA";
}
}
You can use the new await and async feature in .Net:
private void button1_Click(object sender, EventArgs e)
{
EndlessTask();
}
async Task EndlessTask()
{
for(int i = 0; true; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(500);
}
}
[Edit] Note, if you want to get rid of the async warning:
#pragma warning disable 4014
EndlessTask();
#pragma warning restore 4014
i can't setText in textbox with infinite loop
you can never confirm that un till unless you debug your application while using the Thread.Sleep() in infinite loop.
Reason: when you use Thread.Sleep() it makes your Main thread to sleep so it is not a good practice to use Thread.Sleep() . it hangs your UI therefore you can not see the Control Updates on the UI like Label Text update ,TextBox Text Update things like that.
ofcourse you call Application.DoEvents() to refresh the UI but it is not a good practice as there are many other problems.
Solution: i suggest you to use Timer instead of using Thread.Sleep() as it runs in the background so it doesnot hang your UI and also you can see the updates on UI.
Simple Example to show you how to use Timer for updating the Text on Textbox # certain intervals
public partial class Form1 : Form
{
int count = 0;
string text1 = "this is a scrolling text";
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
textBox1.ReadOnly = true;
SetTimer(500);
}
private void SetTimer(int milliseconds)
{
timer1.Tick+=new EventHandler(timer1_Tick);
timer1.Interval = milliseconds;
timer1.Start();
}
private void timer1_Tick(Object o, EventArgs e)
{
if (count < text1.Length)
{
textBox1.Text += text1[count];
count++;
}
else
{
timer1.Stop();
button1.Enabled = true;
textBox1.ReadOnly = false;
}
}
}
I have four buttons that are called "ship1,ship2" etc.
I want them to move to the right side of the form (at the same speed and starting at the same time), and every time I click in one "ship", all the ships should stop.
I know that I need to use a timer (I have the code written that uses threading, but it gives me troubles when stopping the ships.) I don't know how to use timers.
I tried to read the timer info in MDSN but I didn't understand it.
So u can help me?
HERES the code using threading.
I don't want to use it. I need to use a TIMER! (I posted it here because it doesnt give me to post without any code
private bool flag = false;
Thread thr;
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
flag = false;
thr = new Thread(Go);
thr.Start();
}
private delegate void moveBd(Button btn);
void moveButton(Button btn)
{
int x = btn.Location.X;
int y = btn.Location.Y;
btn.Location = new Point(x + 1, y);
}
private void Go()
{
while (((ship1.Location.X + ship1.Size.Width) < this.Size.Width)&&(flag==false))
{
Invoke(new moveBd(moveButton), ship1);
Thread.Sleep(10);
}
MessageBox.Show("U LOOSE");
}
private void button1_Click(object sender, EventArgs e)
{
flag = true;
}
Have you googled Windows.Forms.Timer?
You can start a timer via:
Timer timer = new Timer();
timer.Interval = 1000; //one second
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
timer.Start();
You'll need an event handler to handle the Elapsed event which is where you'll put the code to handle moving the 'Button':
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
MoveButton();
}