使用 WPF 图像进行线程化 (System.InvalidOperationException)

发布于 2024-11-05 02:25:08 字数 1093 浏览 3 评论 0原文

我正在使用一个线程从网站获取图像并将其发送回父窗体 (WPF) 进行显示。我遇到了一个问题并设法将其调试到此示例:

public void Watch()
{
  while (true)
  {
    Bitmap bmp = new Bitmap(1, 1);
    BitmapImage bmpImg = new BitmapImage();

    this.SetImage(bmp, bmpImg);
  }
}

public delegate void SetImageCallback(Bitmap bmp, BitmapImage bmpImg);

private void SetImage(Bitmap bmp, BitmapImage bmpImg)
{
  if (!this.imgVideo.Dispatcher.CheckAccess())
  {
    SetImageCallback del = new SetImageCallback(SetImage);
    this.Dispatcher.Invoke(del, bmp, bmpImg);
  }
  else
  {
    Bitmap bitmap = bmp;
    BitmapImage bitmapImage = bmpImg;
  }
}

请记住 Watch() 在其自己的线程上运行。如果我使用位图对象(我可以在窗口窗体中将其与 PictureBox 一起使用),一切都会很好。也就是说,调试这段代码,当我到达该行

Bitmap bitmap = bmp;

并检查变量 bmp 时,一切都很好并且按预期工作。然而,当我到达下一行

BitmapImage bitmapImage = bmpImg;

并检查变量 bmpImage 时,我得到了大量的 System.InvalidOperationException。当这在实践中被分配给 WPF Image 对象时,它会说“调用线程无法访问该对象,因为不同的线程拥有它。”为什么我在 WPF BitmapImages(需要设置 ImageSource)中遇到此问题,但在 Windows 窗体位图对象(可用于设置 PictureBox)中却没有遇到此问题?如何在 WPF 中解决此问题?

I'm using a thread to get an image from a website and shoot it back to the parent form (WPF) to display. I ran into an issue and have managed to debug it to this example:

public void Watch()
{
  while (true)
  {
    Bitmap bmp = new Bitmap(1, 1);
    BitmapImage bmpImg = new BitmapImage();

    this.SetImage(bmp, bmpImg);
  }
}

public delegate void SetImageCallback(Bitmap bmp, BitmapImage bmpImg);

private void SetImage(Bitmap bmp, BitmapImage bmpImg)
{
  if (!this.imgVideo.Dispatcher.CheckAccess())
  {
    SetImageCallback del = new SetImageCallback(SetImage);
    this.Dispatcher.Invoke(del, bmp, bmpImg);
  }
  else
  {
    Bitmap bitmap = bmp;
    BitmapImage bitmapImage = bmpImg;
  }
}

Keep in mind that Watch() runs on its own thread. If I use the bitmap object (which I can use with PictureBox in Window Forms) everything works great. That is, debugging this code, when I get to the line

Bitmap bitmap = bmp;

And inspect the variable bmp, everything is great and works as expected. HOWEVER, when I get to the next line

BitmapImage bitmapImage = bmpImg;

And inpsect the variable bmpImage, I get a ton of System.InvalidOperationException's. When this is in practice and gets assigned to a WPF Image object, it says that "The calling thread cannot access this object because a different thread owns it." Why am I running into this issue with WPF BitmapImages (which are required to set an ImageSource) but NOT in Windows Forms Bitmap objects (which can be used to set a PictureBox)? How do I fix this in WPF?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

若无相欠,怎会相见 2024-11-12 02:25:08

WPF 中的大多数对象都属于这一类:它们不能在不同线程之间共享。然而,某些低级资源(例如画笔和位图)派生自一个名为Freezable的特殊类,如果冻结,可以在不同线程之间共享。当然,一旦对象被冻结,就不能再以任何方式修改。要冻结可冻结对象,只需调用Freeze,这将防止跨线程异常。

Most objects in WPF are of this category: they cannot be shared between different threads. However certain low-level resources such as brushes and bitmaps are derived from a special class called Freezable that if frozen can be shared between different threads. Of course once an object is frozen is can no longer be modified in any way. To freeze a freezable object simply call Freeze and this will prevent cross-thread exceptions.

落花随流水 2024-11-12 02:25:08

而不是

if (!this.imgVideo.Dispatcher.CheckAccess())
  {
    SetImageCallback del = new SetImageCallback(SetImage);
    this.Dispatcher.Invoke(del, bmp, bmpImg);
  }

尝试使用:

if (!App.Current.Dispatcher.CheckAccess())
                App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<CustomObject>(SetImage),CustomeObjectInstance );

这里的 Cutom 对象将是一个包装器类包装

Bitmap bmp, BitmapImage bmpImg

显然,您的 SetImage 签名将更改为

SetImage(CutomObject custObj)

我尚未测试代码,但这可能会解决问题。
让我们知道这是否有效,以便一些可怜的人可以从这篇文章中受益。
一切顺利!
席德

Instead of

if (!this.imgVideo.Dispatcher.CheckAccess())
  {
    SetImageCallback del = new SetImageCallback(SetImage);
    this.Dispatcher.Invoke(del, bmp, bmpImg);
  }

try using :

if (!App.Current.Dispatcher.CheckAccess())
                App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<CustomObject>(SetImage),CustomeObjectInstance );

Here Cutom object will be a wrapper class wrapping

Bitmap bmp, BitmapImage bmpImg

Obviously, your SetImage signature will change to

SetImage(CutomObject custObj)

I have not tested the code but this may solve the issue.
Let us know if this works so that some poor soul can be benefitted from this post.
All the best!
Sid

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文