在 WPF 中显示字节数组中的图像 - 内存问题
-
27-09-2019 - |
题
我开发了一个应用程序来捕获图像并将其保存到数据库,但我遇到了内存使用问题。在我的域对象上,我有 3 个属性:
Image - 字节数组,内容为jpg
RealImageThumb - 转换为 BitmapImage 并缩小的字节数组,与其他缩略图一起在网格视图中显示给用户
RealImage - 没有设置器,字节数组转换为位图源,当用户将鼠标悬停在工具提示上时,会显示在工具提示中。
我遇到的问题是,如果用户依次将鼠标悬停在每个图像上,内存使用量就会呈螺旋式上升。我意识到当用户将鼠标悬停在生成的位图源上并且内存没有释放时,我尝试为 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;
}
}
解决方案
您需要做三件事:
构建 BitmapImage 时,使用 StreamSource 提供数据。不要使用 UriSource 或将 Uri 传递到构造函数中,这会导致图像被添加到图像缓存中。
在域对象的 RealImage 实现中,存储对 BitmapImage 的 WeakReference,而不是 BitmapImage 本身。当获取 RealImage 时,如果 WeakReference 或 WeakReference.Target 为 null,则创建一个新的 BitmapImage 和一个新的 WeakReference 到它。
使用具有模板切换功能的 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>
现在你可以像这样定义你的工具提示:
<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”。