【问题标题】:WPF System.InvalidOperationException: The calling thread cannot access this object because a different thread owns itWPF System.InvalidOperationException:调用线程无法访问此对象,因为不同的线程拥有它
【发布时间】:2010-08-16 09:18:09
【问题描述】:

在我的 Windows 中,我有一个 TextBox,我想从另一个线程更新(文本属性)。这样做时,我得到 InvalidOperationException(见标题)。我在 google 中找到了不同的链接来解释这一点,但我似乎仍然无法使其工作。

我试过的是这样的:

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 Listener 适配器,监听某个队列,在另一个线程中启动)。

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 组件。

【问题讨论】:

    标签: wpf multithreading


    【解决方案1】:
    Window1.MyWindow.informationTextBox.Dispatcher.Invoke(
        DispatcherPriority.Normal,
        new Action(() => Window1.MyWindow.informationTextBox.Text += value));
    

    【讨论】:

      【解决方案2】:

      嗯,使用Dispatcher.InvokeBeginInvoke 绝对是可行的方法,但除了创建线程之外,您还没有真正展示过太多代码 - 例如,您还没有开始第二个代码块中的线程。

      如果您将Dispatcher.Invoke 代码放在之前获得InvalidOperationException 的位置,应该没问题。

      【讨论】:

      • 我很佩服你的耐心!我相信这是被问到的第 1 次!
      • 嗯,约翰,我知道是这样,我已经搜索了其他问题,但没有找到我想要的答案。我可以去阅读关于 Dispatchers 和线程的大量链接,但事实是我没有时间阅读。我尝试按照其他人的建议进行操作,但它不起作用。 @Jon Skeet:启动我的第二个代码块的线程的代码位于 Spring.NET 程序集中。它们创建侦听 msmq 队列并触发自定义适配器类中的方法的线程。在这个类中,我想更新一个 TextBox。
      • @Lieven:如果您创建一个简短但完整的程序来演示该问题,那将非常非常有帮助。您很可能会发现,通过这样做,您自己会发现问题。完整的程序不需要处理 Spring 等 - 只需创建一个定期更新 UI 的线程或类似的东西。使用Dispatcher 这里的答案-但我们无法在您显示的代码中判断您做错了什么。
      • 嗯,就是这样,我不想要一个定期更新 UI 的线程,我想要另一个线程直接访问 UI 组件。我无法控制这个线程的启动方式。我想我的问题再清楚不过了。有 UI 组件 Textbox 和另一个线程想要设置这个 TextBox 的文本值。
      【解决方案3】:

      对于 WPF,我发现了这个结构:

       BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += ( s, e ) =>
        {
        };
        bw.RunWorkerCompleted += ( s, e ) =>
        {
        };
        bw.RunWorkerAsync();
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-02-13
        • 2012-06-03
        • 1970-01-01
        • 1970-01-01
        • 2022-01-13
        相关资源
        最近更新 更多