XAML 如何在运行时从本地磁盘绑定大量图像?

gg0vcinb  于 2023-01-18  发布在  其他
关注(0)|答案(1)|浏览(130)

我有一个UWP应用程序,它应该在网格中显示来自磁盘的图像。图像路径是在运行时从数据库中获取的。它不能保证用户知道要显示的图像在哪里,所以使用文件夹拾取器是行不通的。
我已经给了这个应用程序broadFileSystemAccess功能。绑定绝对路径似乎仍然不起作用。我现在唯一的解决方案是在我得到路径列表后立即创建一个流作为源的BitmapImage。然而,这带来了一个严重的内存问题。有人能给我一个解决方案吗?
图片库页面.xaml的相关部分:

<GridView
    Grid.Row="4"
    Grid.Column="0"
    Grid.ColumnSpan="5"
    Padding="{StaticResource MediumLeftRightMargin}"
    animations:Connected.ListItemElementName="thumbnailImage"
    animations:Connected.ListItemKey="galleryAnimationKey"
    IsItemClickEnabled="True"
    ItemsSource="{x:Bind ViewModel.Source, Mode=OneWay}"
    SelectionMode="None">
    <i:Interaction.Behaviors>
        <ic:EventTriggerBehavior EventName="ItemClick">
            <ic:InvokeCommandAction Command="{x:Bind ViewModel.ItemSelectedCommand}" />
        </ic:EventTriggerBehavior>
    </i:Interaction.Behaviors>
    <GridView.ItemTemplate>
        <DataTemplate x:DataType="models:GalleryImage">
            <Image
                x:Name="thumbnailImage"
                AutomationProperties.Name="{Binding Name}"
                ToolTipService.ToolTip="{Binding Name}"
                Source="{x:Bind Mode=OneWay, Path=ImgBitmapImage}"
                Style="{StaticResource ThumbnailImageStyle}" />
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

图片库Page.xaml.cs的相关部分:

public ImageGalleryPage()
{
    InitializeComponent();
    Loaded += ImageGalleryPage_Loaded;
}

private async void ImageGalleryPage_Loaded(object sender, RoutedEventArgs e)
{
    Combo_Labels.ItemsSource = await SqlServerClassnamesService.FetchClassnames();
    await ViewModel.LoadDataAsync();
}

ImageGalleryViewModel.cs:

public class ImageGalleryViewModel : ObservableObject
{
    public ObservableCollection<GalleryImage> Source { get; } = new ObservableCollection<GalleryImage>();

    public async Task LoadDataAsync()
    {
        Source.Clear();

        var data = await SqlServerDataService.GetCollection();

        foreach (var item in data)
        {
             await HandleImages(item);
        }
    }

    private async Task HandleImages(GalleryImage img)
    {
        StorageFile file = await StorageFile.GetFileFromPathAsync(img.Path);
        var stream = await file.OpenAsync(FileAccessMode.Read);
        var bitmapImage = new BitmapImage();
        await bitmapImage.SetSourceAsync(stream);
        img.Image = bitmapImage;
        Source.Add(img);
    }
}

GalleryImage.cs:

public class GalleryImage
{
    public string ID { get; set; }

    public string Name { get; set; }

    public string Path { get; set; }

    public BitmapImage Image { get; set; }

    public BitmapImage ImgBitmapImage
    {
        get => Image != null ? Image : new BitmapImage(new Uri(Path));
    }
}
m3eecexj

m3eecexj1#

我假设它的目的是显示某种类型的缩略图网格。但是如果你有其他类型的图像布局,这也应该适用。这也是一般性的建议,而不是实际的代码示例。
首先,你应该避免把所有加载的图片都以全分辨率保存在内存中,你可以把图片缩小到100x100像素,每个缩略图大约40kb,这样你就可以在内存中容纳大量的图片,参见how to resize image
你可以创建UI布局,只要你有一个列表的图像,并更新实际的缩略图异步。至少如果图像被放置在一些类型的规则网格。一个相关的想法是 *UI虚拟化 *,即延迟创建UI对象,直到他们实际可见。一些控件有内置的支持,其他人没有。见Optimizing performance: Controls
您应该尝试在一个或多个后台线程上尽可能多地执行阅读、解码和调整大小等计算密集型工作,以避免阻塞UI线程。这可能需要熟悉图像,以及如何在不同类型的表示之间转换图像。此外,还需要熟悉多线程和线程安全。
你可能还想建立一个系统,尝试智能加载哪些图像。即,优先加载用户可以看到的图像。你可能还想保持用户可能很快查看的全分辨率图像的缓存。

相关问题