4

How do I update my label1 text in the code below ? I am getting a "The calling thread cannot access this object because a different thread owns it" error. I have read that others have used Dispatcher.BeginInvoke but I do not know how to implement it in my code.

public partial class MainWindow : Window
{
    System.Timers.Timer timer;

    [DllImport("user32.dll")]        
    public static extern Boolean GetLastInputInfo(ref tagLASTINPUTINFO plii);

    public struct tagLASTINPUTINFO
    {
        public uint cbSize;
        public Int32 dwTime;
    }

    public MainWindow()
    {
        InitializeComponent();
        StartTimer();
        //webb1.Navigate("http://yahoo.com");
    }

    private void StartTimer()
    {
        timer = new System.Timers.Timer();
        timer.Interval = 100;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        tagLASTINPUTINFO LastInput = new tagLASTINPUTINFO();
        Int32 IdleTime;
        LastInput.cbSize = (uint)Marshal.SizeOf(LastInput);
        LastInput.dwTime = 0;

        if (GetLastInputInfo(ref LastInput))
        {
            IdleTime = System.Environment.TickCount - LastInput.dwTime;
            string s = IdleTime.ToString();
            label1.Content = s;
        } 
    }
}
Mario S
  • 11,715
  • 24
  • 39
  • 47
Bill Greer
  • 3,046
  • 9
  • 49
  • 80
  • possible duplicate of [How to update the GUI from another thread in C#?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) – Gray Aug 16 '13 at 14:45

3 Answers3

6

You can try something like this:

if (GetLastInputInfo(ref LastInput))
{
    IdleTime = System.Environment.TickCount - LastInput.dwTime;
    string s = IdleTime.ToString();

    Dispatcher.BeginInvoke(new Action(() =>
    {
        label1.Content = s;
    }));
}

Read more about Dispatcher.BeginInvoke Method here

Mario S
  • 11,715
  • 24
  • 39
  • 47
2

You'd need to save Dispatcher.CurrentDispatcher from the main thread:

public partial class MainWindow : Window
{
    //...
    public static Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
    //...
}

Then, whenever you need to execute something in the context of the main thread, you do:

MainWindow.dispatcher.Invoke(() => {
   label1.Content = s;
});

Note, Dispatcher.BeginInvoke does it asynchronously, unlike Dispatcher.Invoke. You probably want a synchronous call here. For this case, an async call appears to be ok, but often you may want to update the UI on the main thead, then continue on the current thread knowing the update has been done.

Here's a similar question with a complete example.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • You could also use `Application.Current` instead of saving `Dispatcher.CurrentDispatcher`, as @JMK pointed out, if you have only one UI thread. – noseratio Aug 17 '13 at 12:48
1

There are 2 ways to solve this issue:

First, you can use a DispatcherTimer class instead of the Timer class as demonstrated in this MSDN article, which modifies UI elements in Elapsed event on the Dispatcher thread.

Second, with your existing Timer class, you can use the Dispatcher.BegineInvoke() method as per below code in timer_Elapsed event:

label1.Dispatcher.BeginInvoke(
      System.Windows.Threading.DispatcherPriority.Normal,
      new Action(
        delegate()
        {
          label1.Content = s;
        }
    ));
S2S2
  • 8,322
  • 5
  • 37
  • 65