I am new to C# and also to WPF, I am trying to understand how DispatcherTimer works with GUI (WPF). I want to make my application run a function every 2 seconds but still using the app itself, when I try the following code, the timer starts but I can't use the following Buttons (logout and exit), it's like the app freezes.
public MainLoggedWindow()
{
Globals.mainLoggedWindow = this;
InitializeComponent();
DispatcherTimer dt = new DispatcherTimer();
dt.Tick += new EventHandler(dtTiker);
dt.Interval = new TimeSpan(0, 0, 1);
dt.Start();
}
private void exit_button_Click(object sender, RoutedEventArgs e)
{
logout_button_Click(sender, e);
Environment.Exit(-1);
}
private void logout_button_Click(object sender, RoutedEventArgs e)
{
Globals.LOGGED_IN_USER.logout();
this.Hide();
Globals.mainWindow.Show();
}
private int increment = 0;
private void dtTiker(object sender,EventArgs e)
{
increment++;
Time.Content = increment.ToString();
}
DispatcherTimer runs on the UI thread. It means that when the DispatcherTimer invokes its Tick method the UI thread becomes busy handling that, and it doesn't have time to handle other UI input like button clicks, so the window freezes.
What you could do is increase the interval time for your DispatcherTimer - your question desription says that it's once every two seconds, but your initialisation logic has it every one second: dt.Interval = new TimeSpan(0, 0, 1);
Related
why I can't close this from it gave me an error message (Cross-thread operation not valid: Control 'Form4' accessed from a thread other than the thread it was created on)
my form code;
System.Timers.Timer t = new System.Timers.Timer();
private void Form4_Load(object sender, EventArgs e)
{ myFunction2();}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Elapsed += T_Elapsed;
t.Start();
t.AutoReset = false;
}
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
myFunction();
t.Stop();
t.Enabled = false;
this.Close();
}
private void myFunction()
{
var form6 = new Form6();
//form6.Closed += (s, args) => this.Close();
form6.ShowDialog();}
Edit
I get help from a friend to change this in my code but still, the from4 open and form6 open much time.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
this.Invoke((new Action(() =>
this.Close();
})));
}
private void myFunction()
{
Form6 form6 = new Form6();
form6.ShowDialog();}
Winforms has an "owning-thread" model.
What does that mean?
This model prevents you from accessing an UI component from another thread, not the one which created it.
Why?
Because GUI components are not thread-safe. and should not be, since they'll be much slower. So, WinForms throws an exception like that at you when you try to access a GUI component from another thread - not the owned thread.
But why does this happen toyou?
Because System.Timers.Timer executes its callback in its own thread, which isn't the thread that created the GUI (the main thread of the app). So, you can't access from its callback to any GUI component.
What's the solution?
You can access an GUI component from another thread by a tool called a Dispatcher. But if all you want is a simple timer, you have nicer solution.
Simply use System.Windows.Forms.Timer instead of System.Timers.Timer. This timer is specific to WinForms, and handles all the black work with the dispatcher for you. (Note: WPF has System.Windows.Threading.DispatcherTimer for the same purpose).
But, there's one pitfall: this timer has not AutoReset property. So, you should remove the event by hand after one run, like:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
t.Stop();
this.Close();
}
Since you're closing the window, this is not really needed, but for safety...
Also, note that you don't need both Stop() and Enabled = false together, they are identical (I personally prefer Stop(), I think it's more readable).
In your example (with AutoReset) you didn't need Stop() at all - AutoReset = false run the callback only one time.
Edit:
Although it isn't needed in your case, I append an explanation about "how to use the dispatcher".
Each WinForms' form has a Dispatcher, and some methods related to it. The most important are Invoke() and BeginInvoke() (two overloaded versions, I'm talking about the first which takes System.Delegate).
These methods enable you two access GUI components from not-owning thread, only from the method passed as parameter (in most cases, you must cast it to System.Delegate first).
The difference is, that Invoke() returns only after the method called, while BeginInvoke() is asynchronous; it returns immediately.
So, you can rewrite you code as follows:
private System.Timers.Timer t = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
t.Elapsed += T_Elapsed;
t.Interval = int.Parse(textBox1.Text);
t.AutoReset = false;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
this.Invoke((Action)(() => // You can use `BeginInvoke()` as well
{
this.Close();
}));
// Or
// this.Invoke(new Action(() => // You can use `BeginInvoke()` as well
// {
// this.Close();
// }));
}
Note: Never put long-running tasks inside Invoke() or BeginInvoke()! since they're executed in the owning thread - not in the called thread, they'll freeze the GUI - it's much easier to not use threads at all... Put the calculations in the thread, and call these methods only to update the GUI!
Edit 2:
After I saw what you did with my answer, I was shocked... It seems you even didn't read it! You chose both the solutions: the winforms timer (the good), and the dispatcher (the bas, in this case)! simplify you Tick event so:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
Close();
}
Also, in your myFunction(), you show your second form in modal form. That say, that the method won't return after the second form is closed. See What's the difference between Show(), ShowDialog() and Application.Run() functions? for more details. I think you want to show your second form modeless.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void T_Elapsed(object sender, EventArgs e)
{
if (true)
{
myFunction();
t.Enabled = false;
t.Stop();
}
}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void myFunction()
{
t.Enabled = false;
t.Stop();
this.Hide();
Form6 form6 = new Form6();
form6.ShowDialog();}
I have a function in winform that is executed every x time (eg. every 60 minutes).
And then it does some stuff, then I want it to wait some seconds (using a timer) and then execute do some stuff part2.
private void goToFtp(int time)
{
double interval = time* 60 * 1000;
System.Timers.Timer checkForTime = new System.Timers.Timer(interval);
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed);
checkForTime.Enabled = true;
}
System.Windows.Forms.Timer timerDelayWatcher = new System.Windows.Forms.Timer();
private void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
.......Do some stuff part1
timerDelayWatcher.Tick += new EventHandler(timerDelayWatcher_Tick); // Everytime timer ticks, timer_Tick will be called
timerDelayWatcher.Interval = (1000) * (5);
timerDelayWatcher.Enabled = true;
timerDelayWatcher.Start();
}
private void timerDelayWatcher_Tick(object sender, EventArgs e)
{
timerDelayWatcher.Stop();
.......Do some stuff part2
}
The problem is that the timerDelayWatcher_Tick is not fired...any ideias why?
You need use:
Thread.Sleep(5000);
But first you need add
using System.Threading;
or use
System.Threading.Thread.Sleep(5000);
on 5000 are the time in milliseconds
Sample
private void timerDelayWatcher_Tick(object sender, EventArgs e)
{
timerDelayWatcher.Stop();
System.Threading.Thread.Sleep(5000);
.......Do some stuff part2
}
Try calling the start method on the system.timers.timer firstly, and I would recommend sticking to one type of timer, and pattern of use, say use the system.timer.timer and do the work you need on elapsed, then restart with and wait for the next elapsed event.
Either that or I would suggest looking at the task library and async flow in .net 4/4.5 and as #Ferri suggests using a Sleep
Take also care on loosing reference to the class containing the timerDelayWatcher member.
If it happens the timer is disposed so no more events...
I've been writing a program in c# using windows form(WPF), and I've been stuck on this problem. I'm trying to create a function that will displays an image for a few seconds. This function has to be able to be called from an outside function.
The method should be fairly trivial. When called it would show your image (it's visibility could be false by default). The method could then start a timer, with an interval of the required number of seconds. In the timers elapsed callback you could simply set the images visibility to false.
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 3); //fire after 3 seconds
...
private void ShowMyImage()
{
// logic to show your image
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// logic to hide your image
dispatcherTimer.Stop();
}
i have some usercontrols in my solution, in main window i just change content of ContentControl.
in one usercontrol there is a timer
BringTrinket trincket = new BringTrinket();
trincket.TrincketBringed += new TrincketBringedEventHandler(trincket_TrincketBringed);
this.contentSwitcher.Content = new BringTrinket();
}
void trincket_TrincketBringed(object sender, TrincketEventArgs e)
{
MessageBox.Show(e.TrincketNumber);
this.contentSwitcher.Content = new Loading();
}`
after some event Main Window should change content to (new Loading()), it's OK!
public partial class BringTrinket : UserControl, ISwitchable
{
public event TrincketBringedEventHandler TrincketBringed;
private DispatcherTimer timer;
public BringTrinket()
{
InitializeComponent();
/////////////////////////////////////////////////////////////////////////////
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 1800);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
/////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////
void timer_Tick(object sender, EventArgs e)
{
if (TrincketBringed != null)
{
TrincketBringed(this, new TrincketEventArgs("Hello"));
//TrincketBringed = null;
}
}
But after i've navigataged Timer in BringTrinket still working, how have i dispose that usercontrol? (I know i can set TrincketBringed to null, but timer will still be working)
I think not only timer, but also usercontrol remains in memory
So, your question is how to stop the timer? Use timer.Stop().
BTW, your code would read better if you rename TricketBringed to TrincketBrought.
It looks like your timer is inside of your first usercontrol. You would either need to stop the timer before losing the reference to that object, or better yet, move the timer out of your control.
EDIT #1: I have placed worker.RunWorkerAsync() within my timer loop and my application does not shut down anymore. Although nothing seems to happen now.
For performance reasons i need to replace DispatcherTimers with a other timer that runs in a different thread. There are to much delays / freezes so DispatcherTimer is no longer a option.
I am having problems to actually update my GUI thread, my application always seems to shut down without any warnings / errors.
I have mainly been trying to experiment with BackGroundWorker in attempt to solve my problem. Everything results in a shut down of my application when i launch it.
Some code examples would be greatly apperciated.
Old code dispatcher code:
public void InitializeDispatcherTimerWeging()
{
timerWegingen = new DispatcherTimer();
timerWegingen.Tick += new EventHandler(timerWegingen_Tick);
timerWegingen.Interval = new TimeSpan(0, 0, Convert.ToInt16(minKorteStilstand));
timerWegingen.Start();
}
private void timerWegingen_Tick(object sender, EventArgs e)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
Every 5 seconds the DisplayWegingInfo() and Calculate method should be called upon.
The GUI updates happen in the Calculate method. There a button gets created dynamically and added to a observerableCollection.
Button creation (short version):
public void CreateRegistrationButton()
{
InitializeDispatcherTimerStilstand();
RegistrationButton btn = new RegistrationButton(GlobalObservableCol.regBtns.Count.ToString());
btn.RegistrationCount = GlobalObservableCol.regBtnCount;
btn.Title = "btnRegistration" + GlobalObservableCol.regBtnCount;
btn.BeginStilstand = btn.Time;
GlobalObservableCol.regBtns.Add(btn);
GlobalObservableCol.regBtnCount++;
btn.DuurStilstand = String.Format("{0:D2}:{1:D2}:{2:D2}", 0, 0, 0);
}
New code using threading timer that runs in a different thread then the GUI
public void InitializeDispatcherTimerWeging()
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
worker.RunWorkerAsync();
}
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new Timer(callback);
timerWegingen.Change(0, 5000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
I timer runs in a separate thread then the GUI thread (that dispatcherTimer uses). But i cannot seem to be able to send this update to the UI thread itself so the updates get actually implemented in the UI.
The button gets refilled with new values every 1 sec trough a other timer. "DuurStilstand" is a dependency property
private void FillDuurStilstandRegistrationBtn()
{
TimeSpan tsSec = TimeSpan.FromSeconds(stopWatch.Elapsed.Seconds);
TimeSpan tsMin = TimeSpan.FromMinutes(stopWatch.Elapsed.Minutes);
TimeSpan tsHour = TimeSpan.FromMinutes(stopWatch.Elapsed.Hours);
if (GlobalObservableCol.regBtns.Count >= 1
&& GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].StopWatchActive == true)
{
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].DuurStilstand =
String.Format("{0:D2}:{1:D2}:{2:D2}", tsHour.Hours, tsMin.Minutes, tsSec.Seconds);
}
}
Would i need to use the invoke from Dispatcher in the above method? If so how exactly?
Not sure how to call the ui thread after initializing the doWork method of the BackGroundWorker, my application keeps shutting down after right after start up.
I have tried using Dispatcher.BeginInvoke in several methods but all failed so far. At the moment i have no clue how to implement it.
All the above code is written in a separate c# class.
Best Regards,
Jackz
When I ran my sample of your code, the DisplayWegingInfo() was throwing an exception trying to access UI components. We need to call Invoke() from the Timer thread to update the UI. See DisplayWegingInfo() below. Note: this assumes that CaculateTimeBetweenWegingen() does not interact with the UI.
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new System.Threading.Timer(callback);
timerWegingen.Change(0, 3000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
private void DisplayWegingInfo()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(DisplayWegingInfo));
return;
}
// at this point, we are on the UI thread, and can update the GUI elements
this.label1.Text = DateTime.Now.ToString();
}
private void CaculateTimeBetweenWegingen()
{
Thread.Sleep(1000);
}