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

发布于 2024-09-06 02:08:07 字数 1358 浏览 7 评论 0原文

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

Image - 字节数组,内容是 jpg

RealImageThumb - 字节数组转换为 BitmapImage 并缩小,在网格视图中与其他缩略图一起显示给用户

RealImage - 没有设置器,转换后的字节数组对于位图源,当用户将鼠标悬停在其上时,它会显示在工具提示中。

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

编辑:

这就是你的意思雷吗?我没有在工具提示中显示任何内容,如下所示,但如果我尝试定义 WeakReference,我会收到 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;
            }
        }

I've developed an application to capture and save images to a database, but I'm having an issue with memory usage. On my domain object I have 3 properties:

Image - Byte array, contents are a jpg

RealImageThumb - The byte array converted to a BitmapImage and shrunk, displayed to the user in a gridview with other thumbnails

RealImage - Has no setter, the byte array converted to a bitmap source, this is shown in a tooltip when the user hovers over it.

The issue I have is that if a user hovers over each image in turn the memory usage spirals. I realise that as a user hovers over bitmap sources are generated and the memory isn't freed up, I've tried giving RealImage a backing property and assigning this to null after but again the memory isn't freed up (waiting for the garbage collector?).

edit:

Is this what you meant Ray? I'm not getting anything shown in the tooltip as below, but if I try and define a WeakReference<BitmapImage>, I get the System.WeakReference does not have type parameters error.

  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;
            }
        }

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

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

发布评论

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

评论(1

墨落画卷 2024-09-13 02:08:07

您需要做三件事:

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

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

  3. 使用带有模板切换的 DataTrigger,仅在可见时将 Image 控件包含在可视化树中

    使用带有

以下是第 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>

现在您可以像这样定义 ToolTip:

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

工作原理: 两个 ContentPresenter 相互嵌套:

  • 当外部 ContentPresenter 不可见时,内部 ContentPresenter 将具有 EmptyTemplate,因此没有图像将被加载。
  • 当外部 ContentPresenter 可见时,内部 ContentPresenter 将具有 RealImageTemplate,以便加载并显示图像。

您需要这样做的原因是,工具提示可能会尝试通过在弹出窗口显示后不将其丢弃来优化性能。

更新

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

You will need to do three things:

  1. When constructing your BitmapImage use StreamSource to supply the data. Do not use UriSource or pass a Uri into the constructor, which would cause the image to be added to the image cache.

  2. In your domain object's RealImage implementation, store a WeakReference to your BitmapImage not the BitmapImage itself. When RealImage is fetched, if either WeakReference or WeakReference.Target is null, create a new BitmapImage and a new WeakReference to it.

  3. Use a DataTrigger with template switching to only include your Image control in the visual tree when it is visible

Here are the templates needed for step 3, including the one with the 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>

Now you can define your ToolTip like this:

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

How it works: There are two ContentPresenters inside one another:

  • When the outer ContentPresenter is invisible, the inner ContentPresenter will have EmptyTemplate so no image will be loaded.
  • When the outer ContentPresenter is visible, the inner ContentPresenter will have RealImageTemplate so the image will be loaded and displayed.

The reason you need to do this is that a ToolTip may attempt to optimize performance by not disposing of the Popup once it has been shown.

Update

The code you posted for RealImage ought to work, and is almost exactly what I was thinking of. I realized this morning there is really no need to set BitmapCacheOption or BitmapCreateOption as long as no SourceUri is specified. I've updated my answer to reflect this and also to clarify the WeakReference thing. I also corrected a bug in the template: I was binding to "RealImage" when I should have been binding to "RealImage.Target".

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