我有一个带有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)中传递此值是正确的?但我不知道如何。(我无法使用序列化器 - 因为它正在快速调用操作,这个图像是暂时的/
答案 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>