3

So I'm new to timers and threads and I can't find out how to make a timer that calls a function that edits/modify a control on my MainWindow.

Xaml Code:

<Window x:Class="TimerTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="479" />
</Grid>
</Window>

C# Code:

namespace TimerTest
{
    public partial class MainWindow : Window
    {
        //Declaring my aTimer as global
        public static System.Timers.Timer aTimer;
        //Function that is called
        public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            textBox1.Text += "SomeText ";
        }
        public MainWindow()
        {
            InitializeComponent();
            aTimer = new Timer();
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            aTimer.Interval = 1000; // every 5 seconds
            aTimer.Enabled = true;
        }
    }
}

The Error i get: "The calling thread cannot access this object because a different thread owns it." P.S.: Also I'm not so good with delegates so if you think one might help me in this case, then please post a code sample.

Vlad M.
  • 89
  • 1
  • 2
  • 10
  • Similar to: http://stackoverflow.com/questions/4115598/controls-and-threads/4115634#4115634 – davisoa Mar 02 '12 at 20:26
  • Why use this threaded timer class to access visual controls when there is System.Windows.Forms.Timer? – Martin James Mar 02 '12 at 20:36
  • @davisoa: Bleah, I so hate those code snippets that use a recursive update method. It always takes me 5 minutes to understand what's going on. – Tudor Mar 02 '12 at 20:45
  • @Tudor: but once you do understand, it means you no longer have to worry about calling the method from the right thread. The calling code becomes cleaner, and the complexity is contained. – davisoa Mar 02 '12 at 21:01

3 Answers3

13

Just add:

aTimer.SynchronizingObject = this;

After creating the timer, or use the OTHER type of timer from ToolBox->Components which is System.Windows.Forms.Timer.

The timer you're using at the moment fires on a thread pool thread not a UI one so needs to sync up with the UI thread to work the way you want it.

Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
  • Since I'm still a noobie at c# and .NET I'll have to go with your answer since it is the easiest one, the aTimer.SyncronizingObject = this; did not work :[(incorrect syntax something about a cast) but the Timer in System.Windows.Forms worked like a charm :] Thank you! – Vlad M. Mar 02 '12 at 22:23
4

Only the thread associated to a certain control is allowed to modify that control.

To modify the control from a different thread you will need to wrap your update code inside a delegate and pass it to textBox1.Dispatcher.BeginInvoke:

namespace TimerTest
{
    public partial class MainWindow : Window
    {
        private delegate void updateDelegate(string text);

        private void updateTextBox(string text)
        {
            textBox1.Text += text;
        }

        //Declaring my aTimer as global
        public static System.Timers.Timer aTimer;
        //Function that is called
        public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
             textBox1.Dispatcher.BeginInvoke(
                   new updateDelegate(updateTextBox), "SomeText ");
        }
        public MainWindow()
        {
            InitializeComponent();
            aTimer = new Timer();
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            aTimer.Interval = 1000; // every 5 seconds
            aTimer.Enabled = true;
        }
    }
}
Tudor
  • 61,523
  • 12
  • 102
  • 142
  • Thank you for the usefull info but i'm still shy of using delegates so I'm going with the Timer in System.Windows.Forms, but again thank you I'll make sure to remember this next time. – Vlad M. Mar 02 '12 at 22:25
  • updateDelegate must be defined as: `private **delegate** void updateDelegate(string text);` – matuuar Apr 08 '15 at 15:05
2

The issue is that you're trying to update the UI thread from a different thread. In WinForms you'd have to marshal the call back to the UI thread.

Following the example from WPF .NET Best way to trigger an event every minute, in WPF you can use the Dispatcher Timer:

    public static System.Windows.Threading.DispatcherTimer aTimer;


    public WindowUpdatedByTimer()
    {
        InitializeComponent();

        aTimer = new System.Windows.Threading.DispatcherTimer();
        aTimer.Tick += new EventHandler(OnTimedEvent);
        aTimer.Interval = TimeSpan.FromSeconds(5);
        aTimer.Start();

    }

    public void OnTimedEvent(object source, EventArgs e)
    {
        textBox1.Text += "SomeText ";
    }
Community
  • 1
  • 1
kaj
  • 5,133
  • 2
  • 21
  • 18
  • Thank you for the quick and useful reply! :] It looks a lot like the System.Windows.Forms.Timer but i don't know much about threads so to me at the moment they are about the same :/ god there is a lot to learn xD – Vlad M. Mar 02 '12 at 22:32
  • No problem. Note the Windows timer is targeted for WinForms rather than Wpf. Not that that would stop you using it. Threading is a big topic to get and only worth tackling once you're happy with C# generally. There's one thread for your UI so you want to keep that responsive for the user. See http://msdn.microsoft.com/en-us/library/ms173178.aspx for an explanation if interested – kaj Mar 03 '12 at 07:58