WPF线程错误
本文关键字:错误 线程 WPF | 更新日期: 2023-09-27 18:26:46
我在WPF中使用EmguCV,我发现了这个例子来捕捉图像,我想在我的其他方法Method3()中使用bs1,但我得到了一个错误,即对象属于其他线程,有人知道问题出在哪里吗?bs1毕竟是一个全局变量
BitmapSource bs1;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
capture = new Capture(); ///capture image
timer = new DispatcherTimer(); // timer object
timer.Interval = new TimeSpan(500);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
using ( Image<Bgr, byte> Frame = capture.QueryFrame())
{
if (Frame != null)
{
bs1 = ToBitmapSource(Frame);
webcam.Source = ToBitmapSource(Frame); // ToBitmapSource convert image to bitmapsource webcam is a picture in mainwindow
Frame.Save("fg.jpeg"); //this work but use lot of processing
}
}
}
public void Method3_click (...)
{
use_of_bs1(bs1);
}
private void use_of_bs1()
{
data.Text = "waiting...";
System.Threading.ThreadPool.QueueUserWorkItem(Startwork);
}
private void Startwork(object state)
{
try
{
_work = _worker.bs1_analysis(bs1); // it is where bs1 giving thread errorbs1_analysis is library function
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(new ShowworkInformationDelegate(ShowworkInformation));
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
Dispatcher.BeginInvoke(new ShowWorkInformationDelegate(ShowWorkInformation));
}
///ToBitmapsource函数是
public static BitmapSource ToBitmapSource(Emgu.CV.IImage image)
{
using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap();
BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ptr, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr);
return bs;
}
}
在WPF中,UI元素只能由创建它们的同一线程访问和使用(可冻结元素除外)。在您的代码中,bs1
是在主UI线程中创建的。作为其他线程的计时器无法访问该资源。每当您想对在主UI线程上创建的UI元素执行某些操作时,请执行以下操作:
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(()=>DoSomeCodeWithUIElement()));
如果希望操作同步运行,请使用Dispatcher.Invoke
;如果希望异步调用,则使用Dispatcher.BeginInvoke
。其中DoSomeCodeWithUIElement
是一种访问并最终更新UI元素的方法。
如果您创建了一个wpf资源,并希望在不同的线程上使用它,那么在传递它之前,您可以在对象上调用Freeze()
。这将使它不可变,并且在另一个线程上使用是合法的。
根据您所描述的,bs1与Window.Dispatcher相关联,因此当您在Method3()
中访问它时,会引发异常。为了解决这个问题,你可以做一些类似的事情
public void Method3()
{
Action<BitmapSource> useBs1 = (source) => use_of_bs1(source);
if(Thread.CurrentThread == this.Dispatcher.Thread)
{
useBs1(bs1);
}
else
{
this.Dispatcher.Invoke(DispatcherPriority.Normal,userBs1, bs1);
}
}
在计时器事件(timer_Tick)上,您所在的线程与bs1属于的线程不同
您需要在主线程上执行事件。类似于:
void timer_Tick(object sender, EventArgs e)
{
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(
delegate {
using ( Image<Bgr, byte> Frame = capture.QueryFrame())
{
if (Frame != null)
{
bs1 = ToBitmapSource(Frame);
webcam.Source = ToBitmapSource(Frame); // ToBitmapSource convert image to bitmapsource
Frame.Save("fg.jpeg"); //this work but use lot of processing
}
}}));
}