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

如何在Xamarin.Forms ListView中实现点击Azure Blob图片同行下载?

实现Xamarin.Forms ListView点击Blob图片下载并显示到该行的方案

这个需求我帮不少开发者处理过,一步步来给你拆解实现步骤,代码示例都给你写清楚:

1. 先做些准备工作

  • 安装必要的NuGet包:Azure.Storage.Blobs(微软官方推荐的最新Blob操作包,旧的WindowsAzure.Storage已废弃)和Xamarin.Essentials(帮我们轻松获取本地存储路径)
  • 确保你的Azure Blob容器有合适的访问权限:公开容器可直接用图片URL;私有容器记得给URL加有效SAS Token,或用连接字符串初始化Blob客户端

2. 定义数据模型(必须实现INotifyPropertyChanged)

我们需要一个模型类来存储Blob地址、本地保存路径和下载状态标记,这样UI才能实时响应数据变化:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;

public class ImageItem : INotifyPropertyChanged
{
    private string _blobUrl;
    private string _localImagePath;
    private bool _isDownloading;

    // Azure Blob的图片URL
    public string BlobUrl
    {
        get => _blobUrl;
        set => SetProperty(ref _blobUrl, value);
    }

    // 下载后的本地图片路径
    public string LocalImagePath
    {
        get => _localImagePath;
        set => SetProperty(ref _localImagePath, value);
    }

    // 是否正在下载,用来控制加载指示器的显示
    public bool IsDownloading
    {
        get => _isDownloading;
        set => SetProperty(ref _isDownloading, value);
    }

    // 实现INotifyPropertyChanged的标准代码,通知UI更新
    public event PropertyChangedEventHandler PropertyChanged;
    protected void SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return;
        backingStore = value;
        OnPropertyChanged(propertyName);
    }
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

3. 设计ListView的XAML布局

在页面XAML中,给ListView每一行设置图片控件、点击手势,同时添加下载时的加载指示器:

<!-- 记得给页面设置x:Name="Page",方便绑定ViewModel的命令 -->
<ContentPage x:Name="Page" xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="YourApp.MainPage">
    <ListView ItemsSource="{Binding ImageItems}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <RelativeLayout HeightRequest="200">
                        <!-- 图片显示逻辑:优先用本地路径,没有则显示远程Blob图 -->
                        <Image 
                            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}"
                            RelativeLayout.HeightConstraint="{ConstraintExpression Type=Constant, Constant=200}"
                            Source="{Binding LocalImagePath, TargetNullValue={Binding BlobUrl}}">
                            <!-- 给图片添加点击手势,绑定下载命令 -->
                            <Image.GestureRecognizers>
                                <TapGestureRecognizer 
                                    Command="{Binding Source={x:Reference Page}, Path=BindingContext.DownloadImageCommand}"
                                    CommandParameter="{Binding .}"/>
                            </Image.GestureRecognizers>
                        </Image>
                        <!-- 下载时显示的加载指示器 -->
                        <ActivityIndicator 
                            IsRunning="{Binding IsDownloading}"
                            IsVisible="{Binding IsDownloading}"
                            Color="#0078D7"
                            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5, Constant=-25}"
                            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.5, Constant=-25}"
                            WidthRequest="50" HeightRequest="50"/>
                    </RelativeLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

4. 实现ViewModel里的下载逻辑

在ViewModel中管理列表数据,并实现下载图片的命令:

using Azure.Storage.Blobs;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Xamarin.Essentials;
using System;
using System.Threading.Tasks;

public class MainViewModel : INotifyPropertyChanged
{
    // 绑定到ListView的数据源
    public ObservableCollection<ImageItem> ImageItems { get; set; }
    // 处理图片点击的命令
    public ICommand DownloadImageCommand { get; }

    public MainViewModel()
    {
        ImageItems = new ObservableCollection<ImageItem>();
        // 替换成你从服务器获取的Blob URL列表
        ImageItems.Add(new ImageItem { BlobUrl = "https://yourstorageaccount.blob.core.windows.net/container/yourimage1.jpg" });
        ImageItems.Add(new ImageItem { BlobUrl = "https://yourstorageaccount.blob.core.windows.net/container/yourimage2.jpg" });

        DownloadImageCommand = new Command<ImageItem>(ExecuteDownloadImage);
    }

    private async void ExecuteDownloadImage(ImageItem item)
    {
        // 避免重复下载:正在下载或已下载过则直接返回
        if (item.IsDownloading || !string.IsNullOrEmpty(item.LocalImagePath))
            return;

        item.IsDownloading = true;

        try
        {
            // 1. 生成本地保存路径:缓存目录(卸载应用自动清除),永久保存可改用FileSystem.AppDataDirectory
            var localFileName = $"{Guid.NewGuid()}.jpg";
            var localPath = Path.Combine(FileSystem.CacheDirectory, localFileName);

            // 2. 初始化Blob客户端:私有Blob需用SAS Token或连接字符串创建
            var blobClient = new BlobClient(new Uri(item.BlobUrl));

            // 3. 下载Blob到本地文件
            await blobClient.DownloadToAsync(localPath);

            // 4. 更新模型本地路径,UI自动切换显示本地图片
            item.LocalImagePath = localPath;
        }
        catch (Exception ex)
        {
            // 下载失败提示,可按需调整
            await App.Current.MainPage.DisplayAlert("下载失败", $"无法获取图片:{ex.Message}", "确定");
        }
        finally
        {
            // 无论成败,关闭加载状态
            item.IsDownloading = false;
        }
    }

    // 实现INotifyPropertyChanged,用于ViewModel属性更新通知UI
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

5. 平台权限配置(按需添加)

  • Android:若保存到应用沙盒外路径(如外部存储、相册),需在AndroidManifest.xml添加权限:

    <!-- Android 9及以下 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- Android 13+ 访问相册需要 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    

    仅用FileSystem.CacheDirectory或FileSystem.AppDataDirectory(应用专属目录)时,Android 10+无需额外权限。

  • iOS:若保存到相册,需在Info.plist添加权限描述:

    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>需要访问相册来保存下载的图片</string>
    

    保存到应用沙盒无需权限。

关键细节提醒

  • 用TargetNullValue实现了“远程图→本地图”的自动切换,无需手动修改Image的Source
  • IsDownloading标记既防止重复点击,又能给用户提供加载反馈
  • 本地路径选择:缓存目录适合临时存储,应用目录适合永久保存,相册适合用户需在系统相册查看的图片

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:24:35