Bill Greer
Bill Greer

Reputation: 3156

How to update a control on WPF MainWindow

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;
        } 
    }
}

Upvotes: 4

Views: 3697

Answers (3)

noseratio
noseratio

Reputation: 61656

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.

Upvotes: 2

S2S2
S2S2

Reputation: 8502

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;
        }
    ));

Upvotes: 1

Mario S
Mario S

Reputation: 11945

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

Upvotes: 6

Related Questions