You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 23:47:04