WPF系统。InvalidOperationException:调用线程无法访问此对象,因为另一个线程拥有它。

7
在我的Windows中,我有一个TextBox,我希望从另一个线程更新它的文本属性。但是这样做时,我会收到InvalidOperationException(请参见标题)。我在谷歌上找到了不同的链接来解释这个问题,但似乎仍无法使其工作。

我尝试过的方法如下:

Window1 代码:

private static Window1 _myWindow;
private MessageQueueTemplate _messageQueueTemplate;
private const string LocalTemplateName = "LocalExamSessionAccessCodeMessageQueueTemplate";
private const string RemoteTemplateName = "RemoteExamSessionAccessCodeMessageQueueTemplate";

...
public Window1()
{
    InitializeComponent();
    _myWindow = this;
}

public static Window1 MyWindow
{
    get
    {
        return _myWindow;
    }
}

public void LogText(string text)
{
    informationTextBox.Text += text + Environment.NewLine;
}
...

在另一个类中(实际上是Spring.NET监听器适配器,监听特定队列,在另一个线程中启动)。

var thread = new Thread(
    new ThreadStart(
        delegate()
            {
                Window1.MyWindow.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new Action(
                        delegate()
                            {
                                Window1.MyWindow.LogText(text);
                            }
                        ));
            }
        ));

它不会抛出错误,但是Window1中LogText方法中的文本没有被触发,因此文本没有更新。

所以基本上,我想从另一个运行在另一个线程中的类中更新这个TextBox组件。

3个回答

9
Window1.MyWindow.informationTextBox.Dispatcher.Invoke(
    DispatcherPriority.Normal,
    new Action(() => Window1.MyWindow.informationTextBox.Text += value));

4

使用 Dispatcher.Invoke 或者 BeginInvoke 是正确的方法,但是除了创建线程之外,你并没有展示太多代码 - 例如,在第二个代码块中,你没有 启动 线程。

如果你把 Dispatcher.Invoke 的代码放在之前引发 InvalidOperationException 的地方,应该就可以正常运行了。


我很钦佩你的耐心!我相信这已经是第无数次被问到了! - Manfred
嗯,约翰,我知道这是个问题,我已经搜索了其他的问题,但没有找到我要找的答案。我可以去阅读关于调度程序和线程的众多链接,但事实是我没有时间。我尝试了其他人建议的方法,但它不起作用。 @Jon Skeet:启动我的第二段代码块的线程的代码在Spring.NET程序集中。它们创建监听msmq队列并触发自定义适配器类中方法的线程。在这个类中,我想更新一个TextBox。 - Lieven Cardoen
2
@Lieven:如果您能创建一个简短但完整的程序来演示问题,那将非常有帮助。通过这样做,您可能会发现自己找到了问题所在。完整的程序不需要处理Spring等 - 只需创建一个定期更新UI或类似内容的线程即可。在这里使用Dispatcher 答案-但我们无法确定您在显示的代码中做错了什么。 - Jon Skeet
好的,就是这样,我不想要一个定期更新UI的线程,我想要另一个直接访问UI组件的线程。我无法控制这个线程的启动方式。我认为我的问题已经非常清楚了。有一个UI组件文本框和另一个线程想要设置这个文本框的文本值。 - Lieven Cardoen

0
对于WPF,我发现这个结构:
 BackgroundWorker bw = new BackgroundWorker();
  bw.DoWork += ( s, e ) =>
  {
  };
  bw.RunWorkerCompleted += ( s, e ) =>
  {
  };
  bw.RunWorkerAsync();

成为最有用的。RunWorkerCompleted 块通常会更新 ObservableCollection 或触发 RaisePropertyChangedEvent。


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