WPF图像停止重绘

9
使用WPF和MVVM,我正在尝试将相机图像显示到Image中。 每次相机获取一帧图像时都会调用回调函数:

视图模型

public void OnNewFrame(object sender, EventArgs e)
{
    Camera camera = sender as MyCamera;
    camera.ToBitmap(out _bmpImage);
    RaisePropertyChanged("BMPImage");
}

每帧,我更新变量_bmpImage:
视图模型
private Bitmap _bmpImage;
public Bitmap BMPImage
{
    get
    { return _bmpImage; }
    private set
    { _bmpImage = value; RaisePropertyChanged("BMPImage"); }
}

为了将Bitmap转换为BitmapImage,我使用了一个转换器:

转换器

public class ImageToSource : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter,
                System.Globalization.CultureInfo culture)
        {
            Image image = value as Image;
            if (image != null)
            {
                MemoryStream ms = new MemoryStream();
                image.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.EndInit();
                return bi;
            }
            return null;
        }

        public object ConvertBack(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
  }

最终将其绑定到我的视图中:
 <Image Source="{Binding Main.BMPImage, Converter={StaticResource ImageToSource}}"></Image>

15秒内一切正常,但之后我的图片变成了白色。在转换器中,图片从未为 null,所以相机工作正常。问题是组件 Image 停止重绘。当图片变成白色时,我可以调整窗口大小或移动窗口,这样图片就会恢复正常,因为 Image 会重新绘制。
我做错了什么吗?有办法强制 Image 重新绘制吗?为什么 Image 停止重绘?
谢谢
编辑1:经过一些检查,当图片变成白色时,所有用户界面都会冻结(因此在我调整窗口大小或移动窗口之前无法单击按钮)。
编辑2:正如评论中的 Dennis 建议,我尝试在 ViewModel 中进行转换:为此,我添加了一个代表已转换图像的属性:
 private BitmapImage _testImage;
 public BitmapImage TestImage
 {
      get
      { return _testImage; }
      private set
      { _testImage = value; RaisePropertyChanged("TestImage"); }
 }

我直接将_bmpImage转换为OnNewFrame

public void OnNewFrame(object sender, EventArgs e)
    {
        Camera camera = sender as MyCamera;
        camera.ToBitmap(out _bmpImage);
        //RaisePropertyChanged("BMPImage");
        if (_bmpImage != null)
        {
              // Convertion
              MemoryStream ms = new MemoryStream();
              _bmpImage.Save(ms, ImageFormat.Bmp);
              ms.Seek(0, SeekOrigin.Begin);
              _testImage = new BitmapImage();
              _testImage.BeginInit();
              _testImage.StreamSource = ms;
              _testImage.EndInit();
              RaisePropertyChanged("TestImage");
        }
    }

并且直接将TestImage绑定到我的Image
视图

<Image Source="{Binding Main.TestImage}" />

使用此代码时,我遇到了异常:

Must create DependencySource on same Thread as the DependencyObject

编辑 3

我考虑了你的意见,这是我的新代码:

if (_bmpImage != null)
            {

                // Convertion
                Console.WriteLine("ok");
                MemoryStream ms = new MemoryStream();
                _bmpImage.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                _testImage = new BitmapImage();
                _testImage.BeginInit();
                _testImage.StreamSource = ms;
                _testImage.EndInit();

                ms.Dispose();

                System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                {
                    RaisePropertyChanged("TestImage");
                }));

            }

RaisePropertyChanged("TestImage")处,我遇到了相同的异常。

请注意Edit2和Edit3只是测试,并没有回答我最初的问题。

对于篇幅过长,我深感抱歉。


Bitmap的完整类型名称是什么?您是否尝试在视图模型中执行转换,并从VM公开ImageSource属性而不是Bitmap - Dennis
完整类型为 System.Drawing.Bitmap。我来试一下。 - Titouan56
这似乎正在慢慢变成一个调试会话。在 Stack Overflow 上发布的帖子应该只包含一个问题(而不是“帮我调试”),并且不能随时间过去而频繁更改。也许你可以使用聊天室获得帮助:http://chat.stackoverflow.com/,在找到问题后再修改你的问题。 - Emond
3个回答

2

我敢打赌您的OnNewFrame方法并没有在UI线程上运行。

在您的代码中,更改此行以便在UI线程上运行,通过调用窗口/控件的Dispatcher属性上的Invoke或BeginInvoke方法:

windowOrControlDispatcher.BeginInvoke((Action) (() =>
{
    RaisePropertyChanged("TestImage");
}));

那是个好主意,但在我的视图模型中,我没有用户界面,所以也没有 windowOrControl - Titouan56
但是你正在使用WPF - 你有一个窗口,对吧?你已经创建了一些图像和其他UI元素 - 所以有一个UI线程在运行。你甚至可以尝试Application.Current.Dispatcher... - James Harcourt
这就是我所做的 :) 请查看我的“编辑3” - Titouan56
你只需要使用最初在窗口上创建图像控件的相同调度程序,每个UI元素都有调度程序属性...找到它并不是最困难的部分 :-) 你有一个主窗口吗?如果有,将调度程序的引用传递给ViewModel,或者静态引用它,有许多解决方法... - James Harcourt

0

我终于找到了一个解决方案。我使用另一种方法来转换我的位图(可能更快)

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
{
    Bitmap bmp = value as Bitmap;

    if (bmp != null)
    {
        IntPtr hBitmap = bmp.GetHbitmap(); 
        var drawable = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
              hBitmap,
              IntPtr.Zero,
              Int32Rect.Empty,
              BitmapSizeOptions.FromEmptyOptions());

        DeleteObject(hBitmap);
        bmp.Dispose();
        return drawable;
    }
    return null;
}

0

检查应用程序的内存消耗。

如果位图占用了大量内存,而应用程序没有处理图像和缓冲区,则需要解决该问题。

camera.ToBitmap(out _bmpImage);
// does this clean up the old _bmpImage?

MemoryStream ms = new MemoryStream();
// no dispose!

应用程序可能在收集垃圾而不是更新用户界面上花费时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接