WinUI3/UWP页面导航后ListView图片丢失问题求助
问题:ListView中通过Converter绑定的ImageBrush返回后随机消失
我使用ListView展示数据,DataTemplate内的Rectangle通过Converter绑定Fill属性:当绑定的ImageUrl不为空时返回新的ImageBrush,为空则返回渐变占位画刷。原本运行正常,但导航到其他页面再返回后,部分图片会随机消失,滚动ListView时,消失的条目因虚拟化重建会重新显示图片。
转换器代码(C#)
public class StringImageBrushConverter : IValueConverter { readonly Brush placeholderBrush = new LinearGradientBrush([ new GradientStop() { Color = Color.FromArgb(144, 96, 96, 96), Offset = 0 }, new GradientStop() { Color = Color.FromArgb(10, 96, 96, 96), Offset = 1 }, ], 45); public object Convert(object value, Type targetType, object parameter, string language) => value is string imageUrl ? new ImageBrush() { ImageSource = new BitmapImage(new(imageUrl)) } : placeholderBrush; public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); }
DataTemplate代码(XAML)
<DataTemplate x:DataType="plgm:SearchResult"> <Grid Padding="12"> <Rectangle Width="50" Height="50" HorizontalAlignment="Left" Fill="{x:Bind ImageUrl, Converter={StaticResource StringImageBrushConverter}}" RadiusX="4" RadiusY="4" /> <TextBlock Margin="62,5,90,0" Style="{StaticResource FlyoutPickerTitleTextBlockStyle}" Text="{x:Bind Title}" TextTrimming="CharacterEllipsis" /> <TextBlock Margin="62,26,90,0" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Artists}" TextTrimming="CharacterEllipsis" /> <TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Duration, Converter={StaticResource TimeSpanStringConverter}}" /> </Grid> </DataTemplate>
解决方案
问题根源在于ImageBrush是轻量级对象,未被正确跟踪,页面导航返回后部分ImageBrush资源被释放或绑定失效。以下两种方案可修复该问题:
方案1:使用Image控件替代Rectangle+ImageBrush
直接用Image控件展示图片,占位图通过VisualBrush实现,这样平台会更稳定地管理图片资源:
修改后的DataTemplate(XAML)
<DataTemplate x:DataType="plgm:SearchResult"> <Grid Padding="12"> <Image Width="50" Height="50" HorizontalAlignment="Left" CornerRadius="4" Source="{x:Bind ImageUrl, Converter={StaticResource StringToImageSourceConverter}}" Stretch="UniformToFill" /> <!-- 其余TextBlock代码保持不变 --> <TextBlock Margin="62,5,90,0" Style="{StaticResource FlyoutPickerTitleTextBlockStyle}" Text="{x:Bind Title}" TextTrimming="CharacterEllipsis" /> <TextBlock Margin="62,26,90,0" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Artists}" TextTrimming="CharacterEllipsis" /> <TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Duration, Converter={StaticResource TimeSpanStringConverter}}" /> </Grid> </DataTemplate>
对应的转换器(C#)
public class StringToImageSourceConverter : IValueConverter { readonly ImageSource placeholderImage; public StringToImageSourceConverter() { // 创建渐变占位图 var gradientBrush = new LinearGradientBrush([ new GradientStop() { Color = Color.FromArgb(144, 96, 96, 96), Offset = 0 }, new GradientStop() { Color = Color.FromArgb(10, 96, 96, 96), Offset = 1 }, ], 45); placeholderImage = new DrawingImage(new GeometryDrawing(gradientBrush, null, new RectangleGeometry(new Rect(0,0,1,1)))); placeholderImage.Freeze(); } public object Convert(object value, Type targetType, object parameter, string language) => value is string imageUrl ? CreateBitmapImage(imageUrl) : placeholderImage; private BitmapImage CreateBitmapImage(string url) { var bitmap = new BitmapImage(new Uri(url)); bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.Freeze(); return bitmap; } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); }
方案2:冻结ImageBrush并优化资源管理
若坚持使用Rectangle+ImageBrush,需将创建的ImageBrush冻结,避免资源被意外回收,同时优化BitmapImage的缓存策略:
修改后的转换器代码(C#)
public class StringImageBrushConverter : IValueConverter { readonly Brush placeholderBrush; public StringImageBrushConverter() { placeholderBrush = new LinearGradientBrush([ new GradientStop() { Color = Color.FromArgb(144, 96, 96, 96), Offset = 0 }, new GradientStop() { Color = Color.FromArgb(10, 96, 96, 96), Offset = 1 }, ], 45); // 冻结占位画刷,提升性能 placeholderBrush.Freeze(); } public object Convert(object value, Type targetType, object parameter, string language) { if (value is not string imageUrl) return placeholderBrush; var bitmap = new BitmapImage(new Uri(imageUrl)); bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.Freeze(); var imageBrush = new ImageBrush() { ImageSource = bitmap }; // 冻结画刷,防止资源被回收 imageBrush.Freeze(); return imageBrush; } public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); }
补充说明
ListView虚拟化会复用条目容器,页面导航返回时,未被强引用的ImageBrush可能被GC回收。冻结画刷能让平台更稳定地管理资源,而使用Image控件是更符合平台设计的方案,可从根源避免这类绑定资源问题。
内容的提问来源于stack exchange,提问作者IcySnex
相关产品推荐
相关产品推荐

