线程问题“调用线程无法访问此对象,因为不同的线程拥有它”。有解决方案吗

时间:2011-10-07 07:24:45

标签: c# multithreading xaml

我有一个包含文件名的列表视图。我有另一个列表视图,其中包含重命名这些文件的可能操作。最后,我有一个标签,显示结果的预览。当在每个列表中选择一个对象时,我想显示预览。您只能选择一个文件,但只能选择一个或多个操作。我使用WPF / Xaml作为我的UI。我选择用线程执行我的预览。

以下是我的代码的一部分:

    private Thread _thread;

    public MainWindow()
    {
        InitializeComponent();
        _thread = new Thread(DoWork);
    }

    public void DoWork()
    {
        while (true)
        {
            FileData fileData = listViewFiles.SelectedItem as FileData; // ERROR HERE
            if (fileData != null)
            {
                string name = fileData.FileName;
                foreach (var action in _actionCollection)
                {
                    name = action.Rename(name);
                }
                previewLabel.Content = name;
            }
            Thread.Sleep(1000);
        }
    }

    private void listViewFiles_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        _thread.Start();
    }

在运行时我收到错误“调用线程无法访问此对象,因为另一个线程拥有它。”在FileData fileData = listViewFiles.SelectedItem上作为FileData;线。你知道我该怎么办吗?

2 个答案:

答案 0 :(得分:5)

您无法从nonUI线程修改或访问UI。因此,如果你仍然想要首先使用不同的线程,你需要做的是添加某种模型(有关绑定和模型的更多信息,请尝试搜索“wpf mvvm”),然后将listViewFiles.SelectedItem绑定到某个属性这个模型允许您跨线程访问SelectedValue。其次,你需要将所有改变UI的逻辑分离到方法或使用lambda,所以最后它看起来像这样:

public void DoWork() 
{ 
    while (true) 
    { 
        FileData fileData = Model.SelectedValue;
        if (fileData != null) 
        { 
            string name = fileData.FileName; 
            foreach (var action in _actionCollection) 
            { 
                name = action.Rename(name); 
            } 
            this.Dispatcher.Invoke((Action)()=>  //use Window.Dispatcher
            {
              label3.Content = fileData.FileName; 
              label4.Content = name;
            }); 
        } 
        Thread.Sleep(1000); 
    } 
} 

UPD。关于与UI同步的一些额外的话:在WPF中,每个UI对象都继承自DispatcherObject类。因此,只能从创建此对象的线程进行对此类对象的所有访问,如果要从另一个需要使用DO.Dispatcher.Invoke(Delegate)方法的线程访问DispatcherObject(DO),这会将您的代码排入队列DO线程。因此,总而言之,在UI线程中运行代码需要使用任何UI元素的Dipatcher,在这种情况下我们使用Window的Dispatcher(假设后面的窗口代码中的代码)。

答案 1 :(得分:-1)

简单的答案是你不能这样做:线程A不能直接访问线程B创建的winforms对象(控件)。

实际上,您可以使用委托在另一个线程ala上安全地运行它:

form.Invoke(new MethodInvoker(() => {
       FileData fileData = listViewFiles.SelectedItem as FileData; // ERROR HERE
        if (fileData != null)
        {
            string name = fileData.FileName;
            foreach (var action in _actionCollection)
            {
                name = action.Rename(name);
            }
            previewLabel.Content = name;
        }
   }));

但是,你可能只想 使用后台工作人员:http://msdn.microsoft.com/en-us/library/8xs8549b.aspx

更详细地说:http://weblogs.asp.net/justin_rogers/pages/126345.aspx