0

I'm trying to display an error on my form and use timer to remove the error in a second. I have:

const string sendingError = "currently sending a message please wait";
System.Timers.Timer timer = new System.Timers.Timer(1000);
commandValues.errorList[sendingError] = sendingError;
commandValues.updateErrorList();

this functions as it should by updating a label with the error message

timer.Elapsed += ((source, e) => 
{
    var INDEX = Form.ActiveForm.Controls.IndexOfKey("errorBox");
    Debug.WriteLine(Form.ActiveForm.Controls[INDEX]);
    Form.ActiveForm.Controls[INDEX].Text = "";
    Debug.WriteLine("2" + Form.ActiveForm.Controls[INDEX]);
});

timer.Enabled = true;
timer.Start();

the debug line displays

1System.Windows.Forms.Label, Text: currently sending a message please wait
1System.Windows.Forms.Label, Text: currently sending a message please wait
1System.Windows.Forms.Label, Text: currently sending a message please wait
1System.Windows.Forms.Label, Text: currently sending a message please wait
// etcetera

As you can see, the second debug line is never displaying. Break points agree that it leaves the delegate when I try to change the label.

I'm new to C# so any advice would be appreciated, but specifically I would like to know how to have the main form edited after a timeout and why my attempt is failing.

Damien
  • 263
  • 1
  • 4
  • 15

2 Answers2

3

I'm not positive I understand your problem, but it sounds like you're having an issue updating the UI from a background thread? If so, try this:

timer.Start() starts a new thread separate from your Winform's UI thread, so you may need to Invoke your WinForm's thread in order to see the changes.

timer.Elapsed += ((source, e) =>
{
    var INDEX = Form.ActiveForm.Controls.IndexOfKey("errorBox");
    Debug.WriteLine(Form.ActiveForm.Controls[INDEX]);
    //Invoke the instance of "Form" to process changes on the UI thread
    Form.Invoke((MethodInvoker)delegate
    {
        Form.ActiveForm.Controls[INDEX].Text = "";
    });
    Debug.WriteLine("2" + Form.ActiveForm.Controls[INDEX]);
});
timer.Enabled = true;
timer.Start();

My thoughts on Invoking

If WinForm and NOT Data Bound

myControl.Invoke((MethodInvoker) delegate {/*update UI related values here*/});

or

myForm.Invoke((MethodInvoker) delegate {/*update UI related values here*/});

If WinForm and Data Bound, you may need to update the UI by updating the databound property of your object, or invoke the databound object to update its own property (queueing INotifyPropertyChange or another similar Interface which will force refresh the UI). Note that refactoring your code to data-bind objects to your UI could also prove a permanent solution.

If XAML\WPF, you can use the following snippet to force update your XPF\XAML UI from the base application's dispatcher, like this:

System.Windows.Application.Current.Dispatcher.Invoke((System.Action)delegate {/*update UI related values here*/});

Cheers!

turkinator
  • 905
  • 9
  • 25
0

You should dispatch UI modifications from your Timer thread to UI Thread. Modifying UI elements from another threads is not allowed.

To do it you need to call this.BeginInvoke.

How to update the GUI from another thread in C#?

Community
  • 1
  • 1
Wojciech Kulik
  • 7,823
  • 6
  • 41
  • 67
  • Avoid `BeginInvoke` over `Invoke`, `BeginInvoke` can lead to deadlock on the UI. – Ron Beyer Apr 28 '15 at 16:00
  • @RonBeyer: Backwards perhaps? `Invoke` can deadlock, `BeginInvoke` can overwhelm the UI with a flood of messages, but can't deadlock. – Ben Voigt Apr 28 '15 at 16:01
  • Actually everything depends on how he's going to use it. Other threads always can lead to deadlocks with or without Invoke :-). – Wojciech Kulik Apr 28 '15 at 16:03
  • @BenVoigt No, I have it right. I just ran through this problem with one of my products, it would run for a few days (100% uptime app) and deadlock. Replacing all my BeginInvokes with Invoke resolved the issue, was due to many threads calling BeginInvoke at the same time, probably won't apply here but I avoid it now. – Ron Beyer Apr 28 '15 at 16:03
  • @RonBeyer That is not a deadlock, that is overwhelming the UI with a flood of messages, like Ben said. – Scott Chamberlain Apr 28 '15 at 16:04
  • 1
    @RonBeyer: (If it was a true deadlock) It wasn't `BeginInvoke` causing the deadlock, and you didn't resolve the issue, you just changed the timing enough to hide your race condition. – Ben Voigt Apr 28 '15 at 16:04