在WPF中显示字节数组中的图像 - 内存问题

时间:2010-06-15 13:59:49

标签: wpf image bytearray memory-management bitmapsource

我开发了一个应用程序来捕获图像并将其保存到数据库,但我遇到了内存使用问题。在我的域对象上,我有3个属性:

图像 - 字节数组,内容是jpg

RealImageThumb - 转换为BitmapImage并缩小的字节数组,在带有其他缩略图的gridview中显示给用户

RealImage - 没有setter,字节数组转换为位图源,当用户将鼠标悬停在工具提示上时会显示。

我遇到的问题是,如果用户依次悬停在每个图像上,则内存使用量会呈螺旋式上升。我意识到,当用户盘旋位图源生成并且内存未释放时,我尝试给RealImage一个后备属性并将其分配给null但是内存没有被释放(等待垃圾)集电极?)。

编辑:

这是你的意思雷吗?我没有在工具提示中显示任何内容,如下所示,但如果我尝试定义WeakReference<BitmapImage>,我会得到System.WeakReference没有类型参数错误。

  private WeakReference _realImage;
        public virtual BitmapImage RealImage
        {
            get
            {
                if (_realImage == null || _realImage.Target == null)
                {

                    if (Image == null) return null;
                    var newBitmapImage = new BitmapImage();
                    newBitmapImage.BeginInit();
                    newBitmapImage.CacheOption = BitmapCacheOption.None;
                    newBitmapImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
                    newBitmapImage.StreamSource = new MemoryStream(Image);
                    newBitmapImage.EndInit();
                    _realImage = new WeakReference(newBitmapImage);
                }

                return (BitmapImage)_realImage.Target;
            }
        }

1 个答案:

答案 0 :(得分:3)

您需要做三件事:

  1. 构建BitmapImage时,请使用StreamSource提供数据。不要使用UriSource或将Uri传递给构造函数,这会导致图像被添加到图像缓存中。

  2. 在域对象的RealImage实现中,将WeakReference存储到BitmapImage而不是BitmapImage本身。获取RealImage时,如果WeakReference或WeakReference.Target为null,则创建一个新的BitmapImage和一个新的WeakReference。

  3. 使用带有模板切换功能的DataTrigger,只在可见树中包含Image控件时

  4. 以下是步骤3所需的模板,包括带有DataTrigger的模板:

    <DataTemplate x:Key="EmptyTemplate">
    </DataTemplate>
    
    <DataTemplate x:Key="RealImageTemplate">
      <Image Source="{Binding RealImage.Target}" Width="300" Height="300" />
    </DataTemplate>
    
    <DataTemplate x:Key="RealImageWhenVisible">
    
      <!-- Use EmptyTemplate when I am not visible -->
      <ContentPresenter x:Name="Presenter"
                        Content="{Binding}"
                        ContentTemplate="{StaticResource EmptyTemplate}"/>
    
      <!-- Switch to RealImageTemplate when I am visible -->
      <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsVisible, RelativeSource={RelativeSource Self}}"
                     Value="True">
          <Setter TargetName="Presenter"
                  Property="ContentPresenter.ContentTemplate"
                  Value="{StaticResource RealImageTemplate}" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>
    

    现在您可以像这样定义工具提示:

    <Rectangle Width="40" Height="40" Fill="Blue">
      <Rectangle.ToolTip>
        <ContentPresenter Content="{Binding}"
                          ContentTemplate="{StaticResource RealImageWhenVisible}" />
      </Rectangle.ToolTip>
    </Rectangle>
    

    工作原理:彼此之间有两个ContentPresenters:

    • 当外部ContentPresenter不可见时,内部ContentPresenter将具有EmptyTemplate,因此不会加载任何图像。
    • 当外部ContentPresenter可见时,内部ContentPresenter将具有RealImageTemplate,因此将加载并显示图像。

    您需要执行此操作的原因是,工具提示可能会尝试通过在弹出窗口显示后不处置它来优化性能。

    <强>更新

    您为RealImage发布的代码应该可以工作,而且几乎正是我的想法。我意识到今天早上只要没有指定SourceUri就没有必要设置BitmapCacheOption或BitmapCreateOption。我已经更新了我的答案以反映这一点,并澄清了WeakReference的事情。我还纠正了模板中的一个错误:当我应该绑定到“RealImage.Target”时,我绑定到“RealImage”。