调用线程无法访问此对象(重写但相同的错误)

时间:2011-12-14 15:22:50

标签: c# wpf multithreading

我有一个带有imageViewer控件的MainFrame窗口。在我决定添加ProgressDialog之前,还有我的dll计算图像的变化都工作正常。((想法是 - 首先我通过dll将图像加载到主框架(这仍然可以)。然后如果用户点击按钮然后显示ProgressDialog并在worker.DoWork中通过相同的dllwrapper类创建新图像(我正在使用“new”) 一切似乎都没问题,但是当我试图设置imageviewer控件的currentImage属性时(这只是图像的setter)它向我显示了这个错误! 这是我启动ProgressDialog的userButtonClickHandler的代码:

void OnThumbnailClick(object sender, RoutedEventArgs e)
{
    pd = new ProgressDlg();
    pd.Cancel += CancelProcess;

    int max = 1000;
    System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!

    worker.DoWork += delegate(object s, DoWorkEventArgs args)
    {
        imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
        //what process?? 
    };

    worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
    {
        pd.Close();
    };

    worker.RunWorkerAsync();
    pd.ShowDialog();
}

来自同一MainFrame类的功能可以取消(也可以)

void CancelProcess(object sender, EventArgs e)
{
   worker.CancelAsync();
 }

这是ProgressDlg的类(它只有进度条和取消按钮):

public partial class ProgressDlg : Window
    {
        public ProgressDlg()
        {
            InitializeComponent();
        }
        public string ProgressText
        {
            set
            {
                this.lblProgress.Content = value;
            }
        }
        public int ProgressValue
        {
            set
            {
                this.progress.Value = value;
            }
        }
        public event EventHandler Cancel = delegate { };
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            Cancel(sender, e);
        }
    }
}

我正在处理这个问题(差不多)两天,但仍然无法找到解决方案。如果你有想法,请帮帮我。

1更新 在我看来你对这个线程是正确的 - 当我试图加载以前加载的(初始)图像(从主线程) - 加载好但是如果我正在尝试libWrap它由于进程冲突而失败!

worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
            {
imageViewer.Width = 1000;//work!
  imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!

}

2更新 我试过这个构造OnThumbnailClick

Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
                {
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}

这会导致相同的错误/也许在MainThread(UI)中传递此值是正确的?但我不知道如何。(我无法使用序列化器 - 因为它正在快速调用操作,这个图像是暂时的/

3 个答案:

答案 0 :(得分:2)

您至少需要进行以下更改:

worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
   args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};

worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
  if (args.Error != null)
  { ... }  // handle error
  else if (args.Cancelled)
  { ... } // handle Cancel
  else
  {
    imageViewer.CurrentImage = args.Result;
  }
  pd.Close();
}

我不确定它是否足够但是再试一次。

答案 1 :(得分:2)

WPF无法更改在另一个线程上创建的项目。

因此,如果您在一个线程上创建ImageViewer,则无法在另一个线程上更改它的属性。

相反,使用Dispatcher(主要UI线程的WPF内部消息队列)来更新对象。

或者,使用Henk's Answer在另一个线程上完成工作,但将结果返回给主线程,以便它可以更新ImageViewer的属性

答案 2 :(得分:0)

imageViewer是在应用程序的主线程上创建的(这是合适的,因为它是一个UI控件)。 UI控件只能由创建它的线程访问,并且该线程必须有自己的调度程序(我的意思是消息循环)。

删除线程代码,它将起作用。

如果您希望弹出窗口然后在转换完成时显示图像,则必须将返回的图像存储在变量中,直到返回主线程,然后对imageViewer进行赋值。 / p>