如何在WPF主窗口上更新控件

4

如何在下面的代码中更新标签1文本?我收到了一个“调用线程无法访问此对象,因为不同的线程拥有它”的错误。我已经阅读过其他人使用了Dispatcher.BeginInvoke,但我不知道如何在我的代码中实现它。

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

可能是重复的问题:如何在C#中从另一个线程更新GUI? - Gray
3个回答

6
你可以尝试像这样做:

你可以尝试类似以下的操作:

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

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

点击这里了解更多关于Dispatcher.BeginInvoke方法的信息。


2

您需要在主线程中保存Dispatcher.CurrentDispatcher:

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

然后,每当您需要在主线程的上下文中执行某些操作时,您可以执行以下操作:
MainWindow.dispatcher.Invoke(() => {
   label1.Content = s;
});

请注意,Dispatcher.BeginInvoke是异步执行的,与Dispatcher.Invoke不同。在这种情况下,您可能需要进行同步调用。对于这种情况,异步调用似乎可以,但通常您可能希望在主线程上更新UI,然后继续在当前线程上知道更新完成。
这里有一个类似的问题,包含一个完整的示例。

如果您只有一个 UI 线程,就像 @JMK 指出的那样,您也可以使用 Application.Current 而不是保存 Dispatcher.CurrentDispatcher - noseratio - open to work

1
有两种方法可以解决这个问题:
首先,你可以使用 DispatcherTimer 类来代替 Timer 类,就像 this MSDN article 中演示的那样,在 Elapsed 事件中在 Dispatcher 线程上修改 UI 元素。
其次,对于现有的 Timer 类,你可以在 timer_Elapsed 事件中使用 Dispatcher.BegineInvoke() 方法,如下所示:
label1.Dispatcher.BeginInvoke(
      System.Windows.Threading.DispatcherPriority.Normal,
      new Action(
        delegate()
        {
          label1.Content = s;
        }
    ));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接