c# looping method in WPF - c#

I am sure this may have been answer somewhere but I have searched all over the web and cannot find an obvious solution?
private void button5_Click(object sender, RoutedEventArgs e)
{
if (stopwatch.IsRunning == false)
{
stopwatch.Start();
while (stopwatch.IsRunning == true)
{
label7.Content = stopwatch.Elapsed.ToString();
Thread.Sleep(1000);
}
}
}
label7 does not update and I am assuming its because the while loop never exits?
but I also tried this with no joy?
private void button5_Click(object sender, RoutedEventArgs e)
{
if (stopwatch.IsRunning == false)
{
stopwatch.Start();
}
label7.Content = stopwatch.Elapsed.ToString();
Thread.Sleep(1000);
button5_Click(sender, e);
}

You don't want to do a Thread.Sleep(1000) in there. That will make it so that the UI won't be able to update itself. Instead, use a timer and when the timer tick event fires, change the text in the label.
So for your example, when you define the stopwatch, define its interval and then add an event handler for the 'Tick' event. When it 'ticks' set the content of your label.
Use a DispatcherTimer so that you don't have to shift to the UI thread, as your timer will work within the UI thread for you.

Since you are creating a timer class, you should use the Timer Elapsed event. Update your UI in there.
MSDN Timer Elapsed Event Description
As has been said, sleeping your process thread will essentially freeze the application. Generally avoid it if you can unless you have a very specific reason to sleep a thread. 1 second is also an eternity for a computer, so your elapsed time increments should be relatively small. A user will notice 1 second, they will have a harder time notice increments of .1 s and less.

Related

WPF, how to implement async/await?

I'm learning how to webscrape in WPF. I check the site every 20sec, update my ObservableCollection (myClients) according to search results and display it in Listview (myList). I have 2 Buttons, one to start search and one to stop it.
I didn't know how to implement button autoclick every X sec (which would solve all my problems, am i right?) so i had to use Task.Delay(20000). Program works, it doesn't freeze right at the start like if i had used Thread.Sleep(), but if i press the Stop button and then Start, everything freezes.
I will upload only portion of the code that seems to be the problem. Note that the whole program at the moment is mostly reverse-engineered from several different programs as i am still a beginner.
private async void Button_Click(object sender, RoutedEventArgs e) //Start button
{
string car;
string price;
string link;
wantToAbort = false;
while (!wantToAbort)
{
// ----Simulate GET request----
//-----End GET----
myList.ItemsSource = myClients;
string searchCar = txtBlock.Text + " " + txtBlock2.Text;
var articleNodes = htmlDoc.DocumentNode.SelectNodes($"//*[#id='main_content']/div[1]/div[2]/ul[1]//*[text()[contains(., '{searchCar}')]]");
if (articleNodes != null && articleNodes.Any())
{
foreach (var articleNode in articleNodes)
{
car = WebUtility.HtmlDecode(articleNode.InnerText);
price = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.SelectSingleNode("span").InnerText);
link = WebUtility.HtmlDecode(articleNode.ParentNode.ParentNode.Attributes["href"].Value);
var tempUser = new User(car, price, link);
if (!myClients.Any(x=>x.Link == tempUser.Link))
{
myClients.Insert(0, tempUser); //Inserts new item if Links are different
txtBlock3.Text = "Searching...";
}
}
await Task.Delay(20000); //This seems to be an issue
}
}
}
private void Button_Click_1(object sender, RoutedEventArgs e) //Stop button
{
wantToAbort = true;
txtBlock3.Text = "Ready to search again!";
}
Running a while loop on the UI thread may freeze the application as the UI thread cannot both process UI events and execute a loop or doing anything else simultaneously.
If you want to do something every x seconds you could use a timer as suggested by EJoshuaS. There is a DispatcherTimer class in WPF that fires a Tick event on the UI thread at an interval specified by the Interval property: https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer%28v=vs.110%29.aspx
You don't want to perform the GET request to the web server on the UI thread though so you should probably use a System.Timer.Timer: https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx. This is a different type of timer that runs on a background thread.
Since you can only access UI controls such as TextBlocks and ListBoxes on the thread on which they were originally created - that is the UI thread - you will have to use the dispatcher to marshall any code that access these controls back to the UI thread in your Elapsed event handler:
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
//call the web server here....
//dispatch any access to any UI control
txtBlock3.Dispatcher.Invoke(new Action(() = > { txtBlock3.Text = "Searching..."; }));
}
The golden rule to maintain a responsive application is to execute any long-running code on a background thread but you must only access UI controls back on the UI thread. Please refer to MSDN for more information about the threading model in WPF: https://msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx
DispatcherTimer may be a better solution in this case, like in the below example:
public partial class MainWindow : Window
{
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 220);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
// Do something on your UI
Trace.TraceInformation("Timer expired");
}
}
Basically, this will raise an event at a given interval. Note that Windows Forms also has a timer, as does System.Threading, but you want to make sure you use DispatcherTimer rather than those. In particular, the one from System.Threading tends not to mix well with UIs because it runs its actions on the thread pool and WPF in particular is very fussy about how you update your UI from background threads.
The documentation I link to, as well as this answer, also give details on this.

How do I stop a timer from activating if there is speech recognized?

I have a problem that I am having trouble resolving. Every two seconds a timer_elapsed event fires and moves to the next item in a group of choices. While this is happening, the application is waiting to hear a command using the Microsoft.Speech speech recognition libraries. When it hears that command it is supposed to move to the next group of choices. Sometimes though, the command comes exactly when the timer elapses and because the speech recognition is moving asynchronously the speechrecogonized event will move the choices to the next group while the timer will move within its group.
To control the movement through groups, I have created a set of modes. The timer will call a function based on the current mode. Speech controls what mode you're in and is changed within the speechrecognized event. I have tried putting the timer.stop() at the very beginning of the speechrecognized event but that is useless. They are called together at the same time quite often.
I am a beginning to average programmer. I understand the concepts behind threads but don't really have much experience working with them.
Thank you.
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if(stopTimer)return;
if (mode == Mode.Group1)
{
displayGroup1();
}
else if (mode == Mode.Group2)
{
displayGroup2();
}
else if (mode == Mode.Group3)
{
displayGroup3();
}
}
void sre_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
stopTimer=false;
timer.Enabled = true;
}
void sre_SpeechDetected(object sender, SpeechDetectedEventArgs e)
{
stopTimer=true;
timer.Enabled = false;
}
private void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
timer.Stop();
stopTimer=true;
if (e.Result.Text == "yes")
{
changeMode();
}
}
I have pasted above the most relevant code sections and cleaned out unnecessary details.
Intro - what is it all about
In the following i will explain a basic solution which will attempt to implement a behavior (you might also call it a rule) for the case that Timer.Elapsed events overlap with incoming speech events.
The behaviour demonstrated here can be briefly explained as follows:
While an Timer.Elapsed event is executed, any incoming speech events should be ignored.
When a speech event occurs and no Timer.Elapsed events are executed, stop the timer and handle the speech event. After handling the speech event, restart the timer.
For the following explanation of the code, i assume the reader has an understanding of Microsoft Speech's SpeechRecognitionEngine class and its events.
The code and how it works
The SpeechDetected event handler stops the timer. It also checks, if the Timer.Elapsed event handler is currently executing (via isTimerElapsedHandlerExecuting) - if it does, the ignoreSpeechInput flag will be set to true, indicating that the processed audio data should be ignored.
(The lock i will explain in section 5.)
void sre_SpeechDetected(object sender, SpeechDetectedEventArgs e)
{
lock (_lockObj)
{
timer.Enabled = false;
//
// Given the the explanation above, i should write the code for
// setting of the ignoreSpeechInput flag like this:
// ignoreSpeechInput = isTimerElapsedHandlerExecuting ? true : false;
//
// But obviously that is the same as writing the following...
//
ignoreSpeechInput = isTimerElapsedHandlerExecuting;
}
}
The SpeechRecognized event handler decides based on the ignoreSpeechInput flag, whether the speech input should be ignored or not. It also restarts the timer (which was stopped in the SpeechDetected handler above.
private void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
if (!ignoreSpeechInput)
{
if (e.Result.Text == "yes")
changeMode();
}
timer.Enabled = true;
}
Similar to the SpeechRecognized handler, the handler for the SpeechRecognitionRejected event also needs to restart the timer.
void sre_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
timer.Enabled = true;
}
Beside its main functionality, the Timer.Elapsed event handler will also have to set isTimerElapsedHandlerExecuting accordingly to indicate whether it is executing or whether is finished (i.e., not executing).
You will also note that it explicitly test for Timer.Enabled. The reason for doing so is that there is a possibility that when Timer.Enabled is set back to false one or more Elapsed events are still queued for execution on a ThreadPool thread and which would be executed after Timer.Enabled has been set to false (although i don't really believe that this will happen with your timer interval of 2 seconds).
The try-finally simply ensures that isTimerElapsedHandlerExecuting will be set to false, even if the code in this method throws an exception.
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (_lockObj)
{
if (!timer.Enabled)
return;
isTimerElapsedHandlerExecuting = true;
}
try
{
if (mode == Mode.Group1)
displayGroup1();
else if (mode == Mode.Group2)
displayGroup2();
else if (mode == Mode.Group3)
displayGroup3();
}
finally
{
isTimerElapsedHandlerExecuting = false;
}
}
Why is this lock(_lockObj) being used?
Without that lock your software could run into the following scenario:
sre_SpeechDetected is invoked, executing on thread A. More or less at the same time, timer_Elapsed is invoked, executing on thread B.
timer_Elapsed on thread B sees that Timer.Enabled is true.
A few ticks (CPU clock cycles) later, sre_SpeechDetected on thread A sets Timer.Enabled to false, and it also executes ignoreSpeechInput = isTimerElapsedHandlerExecuting;. isTimerElapsedHandlerExecuting is false at that point in time, thus ignoreSpeechInput becomes false, too.
Again a few ticks later, timer_Elapsed on thread B sets isTimerElapsedHandlerExecuting to true.
Now the speech event handlers incorrectly believe that no Timer.Elapsed handler is executing (because of ignoreSpeechInput == false).
The execution sequence illustrated in points 1. to 4. is called a "Race Condition", and is usually very difficult to debug, as the occurrence of such bugs often depend on a complex combination of certain characteristics of the CPU, overall system load,the behavior of OS and other software running on the system, the weather on that particular day, etc... Of course, this type of bug never occurs on the development and test systems, only on computers of customers ;-)
Summary of the variables used in the code and their default values (declared as private class members):
private readonly object _lockObj = new object();
private bool isTimerElapsedHandlerExecuting = false;
private bool ignoreSpeechInput = false;
private System.Timer timer = ...

prevent this unsafe cross threading

I have this code:
private void Form1_Load(object sender, EventArgs e)
{
/*start update timer*/
System.Timers.Timer updateticker = new System.Timers.Timer();
updateticker.Elapsed += new ElapsedEventHandler(update_overload);
//10 minute ticker
updateticker.Interval = 600000;
//30 sec ticker
updateticker.Interval = 30000;
updateticker.Enabled = true;
System.Timers.Timer guiTimer = new System.Timers.Timer();
guiTimer.Elapsed += new ElapsedEventHandler(idle_display);
//1 minute ticker
guiTimer.Interval = 60000;
//30 sec ticker
//updateticker.Interval = 30000;
guiTimer.Enabled = true;
}
//run front end idle timer
public void idle_display(object source, ElapsedEventArgs e)
{
if (minutes_left > 0) {
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = "Time until next automatic update: "+ minutes_left + " minutes.";
}
Visual studio is flagging the final line of the second function as unsafe cross threading. Can anyone suggest how I rewrite this to solve the issue?
Cheers
Use System.Windows.Forms.Timer instead of System.Timers.Timer
or use SynchronizingObject property of Timer.
When SynchronizingObject is null, the method that handles the Elapsed event is called on a thread from the system-thread pool. For more information on system-thread pools, see ThreadPool.
When the Elapsed event is handled by a visual Windows Forms component, such as a button, accessing the component through the system-thread pool might result in an exception or just might not work. Avoid this effect by setting SynchronizingObject to a Windows Forms component, which causes the method that handles the Elapsed event to be called on the same thread that the component was created on.
As others suggested, use System.Windows.Forms.Timer. Unlike System.Threading.Timer, which operates on Thread Pool threads, it guarantees you that the tick event is invoked on the UI thread.
The UI thread is the only thread allows to modify the UI. This is why you get an exception trying to write lbl_dyn_status.Text.
Use System.Windows.Forms.Timer timer instead of System.Timers.Timer.
Change the handler to
public void IdleDisplay(object source, ElapsedEventArgs e)
{
if (lbl_dyn_status.InvokeRequired)
{
this.Invoke(IdleDisplay)
}
else
{
if (minutes_left > 0)
{
minutes_left = minutes_left - 1;
}
lbl_dyn_status.Text = string.Format(
"Time until next automatic update: {0} minutes.",
minutes_left);
}
}
This way allows you to use the thread free System.Threading.Timer but checks for a cross-thread call in the handler. If detected the call is invoked on the main GUI thread, via the Form class.
This is described on MSDN in more detail here.
More generally, you should not use timers to count time like this. The more thread bound your timer is, the more it is likely to diverge from real elapsed time. You can use your timer to schedule an update of your clock but, you should calculate elapsed time since some fixed point rather than using an iterative counter.

Synchronizing Forms.Timer and Diagnostics.Stopwatch

I have a function (say foo())that will be called from time to time with a variable interval. When it is called, it checks the time and takes an action accordingly.
I have done this in the following way:
A Forms.Timer object invokes the function when required
A Diagnostics.Stopwatch object is used within the function for the purpose of determining the time and deciding what to do.
However I have the following problem: when foo() is called by Timer's callback, the ElapsedMilliseconds value of stopwatch object is usually lower than expected. For example, timer is set to 1000 so after 1000 ms foo() is called, but within foo() body ElapsedMilliseconds return 900 therefore foo behaves as if the elapsed time was 900 (although it should take the action A because 1000 ms actually elapsed, it does not)
How can I synchronize timer and stopwatch in such case that ElapsedMilliseconds have a consistent value with timer?
EDIT: Some Code
Some sample code to explain what is my problem:
//foo is the function that is called by timer's callback
public void foo()
{
//Let's see what time it is:
long currentTime = stopwatch.ElapsedMilliseconds();
Item = getCurrentItem(currentTime);
Item.Draw();
}
//this is the callback of timer
private void timer1_Tick(object sender, EventArgs e)
{
//set the timer for next time
timer1.Interval = Intervals[periodIdx++];
foo();
}
This is supposed to draw something else each time when an interval is completed, however since ElapsedMilliseconds return an earlier value than timer claims, although the interval is over, next item isn't drawn
You get the big difference because you start the timer somewhere within the 1/64 second interval. You'll get better results with this:
private void StartTimers() {
int tick = Environment.TickCount;
while (Environment.TickCount == tick) Thread.Sleep(0);
timer1.Enabled = true;
stopwatch.Start();
}
Where the while() loop improves the odds that the timer gets started at the start of a 1/64 timer tick. Just improves, no guarantees. And you can't do anything about the Tick event firing late, it entirely depends on the responsiveness of your UI thread. It is however always late. Don't use this code, write your code so you don't care that these timers are not in sync. You may have to reduce the timer's Interval to accomplish that, it isn't clear from the question.
You aren't going to have much success with this approach. You're not starting each timer at the exact same time and you're not checking them at the exact same time (there some passage of time between the Timer firing it's event and your code querying the Stopwatch).
Pick a single timer and base everything off of it if you want things in sync. For example, if you want to go with the Forms.Timer, in your event handler for it just increment a counter variable - that will tell you how many times your handler has been called and, effectively, how much time the Forms.Timer says has passed. Here's an example (I'll leave it to you to handle the case of the timer ticking long enough that the counter exceeds long.MaxValue)
public void foo()
{
Item = getCurrentItem(totalElapsed);
Item.Draw();
}
long totalElapsed = 0;
private void timer1_Tick(object sender, EventArgs e)
{
totalElapsed += timer1.Interval;
//set the timer for next time
timer1.Interval = Intervals[periodIdx++];
foo();
}

How do I add a delay after a count down timer

I am using a DispatcherTimer to perform a count down before triggering the release on a camera. The UpdateCountdown method is used to change the image displayed to the user before the camera fires. Rather than having the TakePicture method execute immediately, I would like have a slight delay after the counter reaches zero and the last image is displayed.
The code shown below results in the pause occurring at the _countdown = 1 point. While the final image displays and TakePicture() fires almost simultaneously (I think TakePicture happens first).
_countdownTimer = new DispatcherTimer();
_countdownTimer.Interval = TimeSpan.FromSeconds(1);
_countdownTimer.Tick += new EventHandler(delegate(object s, EventArgs a)
{ UpdateCountdown(); } );
_countdownTimer.Tick += new EventHandler(delegate(object s, EventArgs a)
{if (_countdown == _countdownMax)
{
System.Threading.Thread.Sleep(2000); // Slight delay before taking picture
Camera.TakePicture();
} });
}
public void StartCountdown()
{
if (doCount)
{
doCount = false;
UpdateCountdown();
_countdownTimer.Start();
}
}
private void UpdateCountdown()
{
_countdown--;
CountdownImage = _countDownImages[_countdown]; // Data bound to Image Control
if (_countdown == 0)
{
_countdown = _countdownMax;
_countdownTimer.Stop();
doCount = true;
}
What am I not taking into account with my timing?
The UI does not update immediately when you change control properties - it only updates when the thread becomes idle (that is, after all your event handlers finish executing).
Thread.Sleep blocks the thread, the event handlers don't finish executing and UI isn't redrawn.
You have to either use another timer (start a new timer on the last tick of the existing timer and call TakePicture on teh new timer's tick) or, even better, use the last tick of the existing timer - update UI when (_countdown <= _countdownMax), take picture when (_countdown == _countdownMax + 1).
Why not just make your display always show 1 less than the number of seconds remaining. That way when you get to zero, (obviously with a Math.Max(0, _countdown) to prevent showing -1) it will seem like the time has run out even though there's one more second to go.
Edit: What I meant to imply but did not state - was that you could then just have one Tick handler and not use Sleep at all which will just wind up blocking the UI anyway which will probably block your UI from updating.
I don't think that events guarantee that event handlers are triggered in the order that they are registered. Try
_countdownTimer.Tick += new EventHandler(delegate(object s, EventArgs a)
{
UpdateCountdown();
if (_countdown == _countdownMax)
{
System.Threading.Thread.Sleep(2000); // Slight delay before taking picture
Camera.TakePicture();
}
});
}

Categories

Resources