Damien
Damien

Reputation: 273

How to edit the main form in C# from a timer?

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.

Upvotes: 0

Views: 2698

Answers (2)

turkinator
turkinator

Reputation: 925

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!

Upvotes: 3

Wojciech Kulik
Wojciech Kulik

Reputation: 8500

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#?

Upvotes: 0

Related Questions