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

UWP GridView增量加载图片不显示及逐批加载16张实现问题

问题根因
  • XAML存在语法错误:Image节点的x:Name="cover缺少闭合双引号,会导致XAML解析失败,控件无法正常渲染
  • 数据类型不匹配:原ItemsToShow继承自ObservableCollection<string>,集合存储的是base64字符串,但DataTemplate里Image的Source绑定的是Image属性,字符串对象不存在该属性,绑定直接失效;同时代码里读取item.Path时从未给该属性赋值,会触发空引用异常
  • 增量加载逻辑混乱:每次触发加载都会全量扫描所有文件夹、遍历全部435个文件,没有记录当前加载位置;HasMoreItems判断写死为1000,和实际文件总数不匹配;内层循环重复添加同一项,计数逻辑完全错误
  • 性能与线程问题:所有文件IO操作重复执行,BitmapImage创建未做线程适配,容易触发跨线程访问异常,同时硬编码从集合类获取页面ProgressRing控件,耦合度极高容易出错
  • 文件排序失效:原代码注释掉了获取文件修改时间的逻辑,排序依据的LastModified属性全是默认值,实际没有按修改时间排序
正确实现方案

1. 修正XAML代码

补全XAML语法错误,配置GridView的增量加载参数,固定每次预取批次大小:

<GridView  
    x:Name="komikGridView" 
    Loaded="komikGridView_Loaded"
    IncrementalLoadingThreshold="2"
    DataFetchSize="16"
    SelectionMode="None"
    IsItemClickEnabled="False">
    <GridView.Resources>
        <DataTemplate x:Key="DataTemplatekomikGridView">
            <Grid x:Name="komikGrid1">
                <Image
                    x:Name="cover"
                    Width="160"
                    Height="235"
                    Stretch="UniformToFill"
                    Source="{Binding Image}" />
            </Grid>
        </DataTemplate>
    </GridView.Resources>
    <GridView.ItemTemplate>
        <StaticResource ResourceKey="DataTemplatekomikGridView"/>
    </GridView.ItemTemplate>
</GridView>
<!-- 页面单独放置加载进度环,不要在集合类里直接操作UI元素 -->
<ProgressRing 
    x:Name="loading" 
    Grid.RowSpan="2"
    Width="50" 
    Height="50" 
    Visibility="Collapsed"
    IsActive="False"/>

参数说明:DataFetchSize="16"指定每次预取16项,IncrementalLoadingThreshold="2"表示滚动到距离列表剩余2项时触发下一次加载。

2. 定义Book模型类

public class Book
{
    public string Name { get; set; }
    public ImageSource Image { get; set; }
}

3. 重写增量加载集合类

把一次性的文件扫描逻辑放到构造函数里仅执行一次,记录总文件数和当前加载位置,固定每批加载16张:

public class KomikIncrementalCollection : ObservableCollection<Book>, ISupportIncrementalLoading
{
    private readonly List<StorageFile> _sortedThumbFiles = new List<StorageFile>();
    private int _currentLoadedIndex = 0;
    private const int BatchSize = 16; // 固定每批加载16张
    private bool _fileListInited = false;

    public bool HasMoreItems => _fileListInited && _currentLoadedIndex < _sortedThumbFiles.Count;

    // 构造函数里一次性完成文件夹扫描、文件排序,全局只执行一次
    public KomikIncrementalCollection()
    {
        InitFileListAsync().ConfigureAwait(false);
    }

    private async Task InitFileListAsync()
    {
        StorageFolder installedLocation = ApplicationData.Current.LocalFolder;
        StorageFolder library = await installedLocation.CreateFolderAsync("library", CreationCollisionOption.OpenIfExists);
        StorageFolder komik = await library.CreateFolderAsync("komik", CreationCollisionOption.OpenIfExists);
        IReadOnlyList<StorageFile> files = await komik.GetFilesAsync();

        // 正确按修改时间倒序排序
        List<Temp> sortingFiles = new List<Temp>();
        foreach (StorageFile file in files)
        {
            BasicProperties prop = await file.GetBasicPropertiesAsync();
            sortingFiles.Add(new Temp
            {
                File = file,
                LastModified = prop.DateModified.DateTime,
                Name = file.Name
            });
        }

        // 获取对应缩略图文件夹的文件列表
        StorageFolder thumbfolder = await installedLocation.CreateFolderAsync("thumb", CreationCollisionOption.OpenIfExists);
        StorageFolder komikthumb = await thumbfolder.CreateFolderAsync("komik", CreationCollisionOption.OpenIfExists);
        
        foreach (Temp item in sortingFiles.OrderByDescending(x => x.LastModified))
        {
            // 只添加存在对应缩略图的文件,避免加载时报错
            if (await komikthumb.TryGetItemAsync(item.Name + ".png") is StorageFile thumbFile)
            {
                _sortedThumbFiles.Add(thumbFile);
            }
        }
        _fileListInited = true;
    }

    public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
    {
        CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
        return Task.Run<LoadMoreItemsResult>(async () =>
        {
            // 等待文件列表初始化完成
            while (!_fileListInited)
            {
                await Task.Delay(10);
            }
            // 计算本次要加载的数量:取批次大小和剩余文件数的最小值,避免越界
            int itemsToLoad = Math.Min(BatchSize, _sortedThumbFiles.Count - _currentLoadedIndex);
            List<Book> currentBatch = new List<Book>();

            for (int i = 0; i < itemsToLoad; i++)
            {
                StorageFile thumbFile = _sortedThumbFiles[_currentLoadedIndex + i];
                Book book = new Book
                {
                    Name = thumbFile.DisplayName.Replace(".png", "")
                };

                // 回到UI线程创建BitmapImage,避免跨线程异常
                await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
                {
                    BitmapImage bi = new BitmapImage();
                    using (IRandomAccessStream stream = await thumbFile.OpenAsync(FileAccessMode.Read))
                    {
                        await bi.SetSourceAsync(stream);
                    }
                    book.Image = bi;
                });
                currentBatch.Add(book);
            }

            // 回到UI线程把数据加入集合
            await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                foreach (Book item in currentBatch)
                {
                    this.Add(item);
                }
            });

            _currentLoadedIndex += itemsToLoad;
            return new LoadMoreItemsResult() { Count = (uint)itemsToLoad };
        }).AsAsyncOperation();
    }

    private class Temp
    {
        public StorageFile File { get; set; }
        public DateTime LastModified { get; set; }
        public string Name { get; set; }
    }
}

4. 修正页面后台代码

在页面层控制加载状态,避免UI和数据逻辑耦合:

private async void komikGridView_Loaded(object sender, RoutedEventArgs e)
{
    // 避免重复触发加载事件导致重复初始化
    if (komikGridView.ItemsSource != null) return;
    
    loading.IsActive = true;
    loading.Visibility = Visibility.Visible;

    KomikIncrementalCollection collection = new KomikIncrementalCollection();
    komikGridView.ItemsSource = collection;
    
    // 等待第一批次16张加载完成后隐藏进度环
    while (collection.Count < 16 && collection.HasMoreItems)
    {
        await Task.Delay(10);
    }

    loading.IsActive = false;
    loading.Visibility = Visibility.Collapsed;
}
实现效果说明
  • 首次进入页面自动加载第一批次16张图片
  • 滚动到距离列表底部2项位置时,自动加载下一批次16张图片,直到全部435张图片加载完成
  • 所有文件扫描、排序逻辑仅在集合初始化时执行一次,不会重复扫描文件夹造成性能浪费
  • 数据类型和绑定路径完全匹配,图片可以正常渲染
  • 不会出现重复添加、索引越界、加载数量错误的问题
  • 所有UI相关操作都在UI线程执行,不会触发跨线程访问异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:24:25