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

桌面图片应用加载大量图片时UI卡顿:如何实现异步加载?

问题场景

我正在开发一款简易桌面图片应用,目前整体进展顺利。该应用从文件夹中获取图片,通过ItemsControl按(MM)YYYY格式的Key分组展示,数据基于StorageItem类。少量图片时应用运行正常,但图片数量较多时页面会卡顿,原因是Icon属性(BitmapImage类型)在MediaPage的GridView展示前就已全部加载,阻塞了UI线程。

相关XAML代码:

<ItemsControl x:Name="test_control">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Margin="10">
                <!-- Display the Key (Year and Month) -->
                <TextBlock Text="{Binding Key}" FontSize="20" FontWeight="Bold" Margin="0,0,0,10" />
                <GridView 
                    ItemsSource="{Binding Items}"
                    SelectionMode="None"
                    IsItemClickEnabled="True"
                    RightTapped="GridView_RightTapped"
                    DoubleTapped="GridView_DoubleTapped">
                    <GridView.ItemsPanel>
                        <ItemsPanelTemplate>
                            <ItemsWrapGrid MaximumRowsOrColumns="20" Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </GridView.ItemsPanel>
                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <Grid Width="100" Height="100" ToolTipService.ToolTip="{Binding FullPath}">
                                <StackPanel Orientation="Vertical">
                                    <Image Height="90" Width="90" Source="{Binding Icon}" Stretch="UniformToFill"/>
                                </StackPanel>
                                <ProgressRing 
                                    VerticalAlignment="Bottom"
                                    HorizontalAlignment="Right"
                                    Visibility="{Binding ProgressBarVisiable}"
                                    Value="{Binding Value}"
                                    IsIndeterminate="False"
                                    Height="25"
                                    Width="25"/>
                                <FontIcon HorizontalAlignment="Left"
                                    VerticalAlignment="Bottom"
                                    Glyph="{Binding StatusFont}"/>
                            </Grid>
                        </DataTemplate>
                    </GridView.ItemTemplate>
                </GridView>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

核心后台代码片段:

  • MediaPage.cs
test.ItemsSource = App.AllImages;
  • MainPage.cs
App.AllImages = new ObservableCollection<MediaDynamicView>();
PublicMethods.GetAllImages(AllImages);
  • ImageItem.cs
// more properties...
public virtual BitmapImage Icon
{
    get { return _icon; }
    set { _icon = value; OnPropertyChanged(); }
}
异步加载Icon的解决方案

1. 修改ImageItem实现延迟异步加载

将Icon的加载逻辑改为异步,仅当Item被UI渲染时才触发加载,同时维护加载状态控制ProgressRing显示:

private BitmapImage _icon;
private bool _isLoading;

// 新增加载状态属性,绑定到ProgressRing的可见性
public bool IsLoading
{
    get => _isLoading;
    set { _isLoading = value; OnPropertyChanged(); }
}

public virtual BitmapImage Icon
{
    get
    {
        // 仅当未加载且未在加载中时触发异步加载
        if (_icon == null && !IsLoading)
        {
            _ = LoadIconAsync();
        }
        return _icon;
    }
    set { _icon = value; OnPropertyChanged(); }
}

private async Task LoadIconAsync()
{
    IsLoading = true;
    try
    {
        var bitmap = new BitmapImage();
        // 设置解码尺寸,匹配UI上的图片大小,减少内存占用
        bitmap.DecodePixelWidth = 90;

        // UWP环境下的加载方式
        using (var stream = await StorageFile.GetFileFromPathAsync(FullPath).AsTask())
        using (var randomAccessStream = await stream.OpenAsync(FileAccessMode.Read))
        {
            await bitmap.SetSourceAsync(randomAccessStream);
        }

        // 如果是WPF环境,替换为以下代码:
        // bitmap.BeginInit();
        // bitmap.CacheOption = BitmapCacheOption.OnLoad;
        // bitmap.UriSource = new Uri(FullPath);
        // bitmap.DecodePixelWidth = 90;
        // bitmap.EndInit();

        Icon = bitmap;
    }
    catch (Exception)
    {
        // 加载失败时设置默认图标
        Icon = new BitmapImage(new Uri("ms-appx:///Assets/DefaultThumbnail.png"));
    }
    finally
    {
        IsLoading = false;
    }
}

2. 更新XAML绑定

修改ProgressRing的绑定,关联新增的IsLoading属性,同时使用布尔值转可见性的转换器:

首先在页面资源中定义转换器(若项目中没有):

<Page.Resources>
    <local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Page.Resources>

转换器代码:

public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return (value is bool loading && loading) ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return (value is Visibility visibility && visibility == Visibility.Visible);
    }
}

然后更新ProgressRing的绑定:

<ProgressRing 
    VerticalAlignment="Bottom"
    HorizontalAlignment="Right"
    Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}"
    IsIndeterminate="True"
    Height="25"
    Width="25"/>

3. 优化UI虚拟化

确保ItemsControl和GridView的虚拟化正常工作,减少不必要的Item渲染:

  • 给外层ItemsControl添加虚拟化面板:
<ItemsControl x:Name="test_control">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <!-- 原有ItemTemplate不变 -->
</ItemsControl>
  • GridView的ItemsWrapGrid默认支持虚拟化,无需额外修改,但要避免给ItemContainer添加复杂布局或样式,影响虚拟化效率。

4. 批量加载优化(可选)

如果希望控制并发加载数量,避免同时加载过多图片导致资源占用过高,可以在ImageItem中加入静态的信号量控制:

private static readonly SemaphoreSlim _loadSemaphore = new SemaphoreSlim(5); // 最多同时加载5张

private async Task LoadIconAsync()
{
    IsLoading = true;
    await _loadSemaphore.WaitAsync();
    try
    {
        // 原有加载逻辑
    }
    finally
    {
        IsLoading = false;
        _loadSemaphore.Release();
    }
}

内容的提问来源于stack exchange,提问作者Ariel P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:59:55