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();
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With