Skip to content
On this page

檔案與資料夾選擇功能

說明

檔案選擇功能是現代應用程式中與使用者互動最頻繁的行為之一。在 Avalonia 11 中,這項功能透過 StorageProvider 實現,它不僅支援傳統桌面路徑,也相容於行動端與 Web 的抽象檔案系統。 將此功能封裝為 Service 模式,實作流程主要分為以下五個步驟:

  1. 定義服務介面:建立 IStorageService 介面,規範開啟檔案與選擇資料夾的非同步方法,提升程式碼的可測試性。
  2. 實作存取邏輯:撰寫 StorageService 類別,透過 Application.Current 動態獲取當前視窗的 TopLevel 物件以存取底層 API。
  3. 注入聚合服務:為了避免 ViewModel 建構函式過於臃腫,將其註冊至 DI 容器(如 IServiceProvider)。
  4. View/ViewModel 呼叫:在 ViewModel 中透過 RelayCommand 調用服務方法,獲取路徑後進行後續的業務邏輯處理(如讀取檔案或儲存路徑)。

定義服務介面

C#
//IStorageService.cs
using System.Threading.Tasks;

namespace AvaloniaSideBarEx.Services;

public interface IStorageService
{
    Task<string?> OpenFilePickerAsync(string title = "請選擇檔案");
    Task<string?> OpenFolderPickerAsync(string title = "請選擇資料夾");
}

實作存取邏輯

C#
//StorageService.cs
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage;

namespace AvaloniaSideBarEx.Services;

public class StorageService : IStorageService
{
    private IStorageProvider? GetStorageProvider()
    {        // 針對桌面版 (Windows/macOS/Linux) 的處理方式
        if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            return TopLevel.GetTopLevel(desktop.MainWindow)?.StorageProvider;
        }
        return null;
    }
    public async Task<string?> OpenFilePickerAsync(string title = "請選擇檔案")
    {
        var storageProvider = GetStorageProvider();
        if (storageProvider == null) return null;
        var result = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
        {
            Title = title,
            AllowMultiple = false,
            FileTypeFilter = new[] { FilePickerFileTypes.All }
        });
        return result.FirstOrDefault()?.Path.LocalPath;
    }
    public async Task<string?> OpenFolderPickerAsync(string title = "請選擇資料夾")
    {
        var storageProvider = GetStorageProvider();
        if (storageProvider == null) return null;

        var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
        {
            Title = title,
            AllowMultiple = false
        });
        return result.FirstOrDefault()?.Path.LocalPath;
    }
}

注入聚合服務

c#
//App.axaml.cs
collection.AddSingleton<IStorageService, StorageService>();

View/ViewModel 呼叫

C#
//HomeViewModel.cs
using System;
using System.Threading.Tasks;
using AvaloniaSideBarEx.Services;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;

namespace AvaloniaSideBarEx.ViewModels;

public partial class HomeViewModel(IServiceProvider services): ViewModelBase
{
    public string WelcomeMessage => "Home頁面";
    private INotificationService NotificationService => services.GetRequiredService<INotificationService>();
    private IStorageService StorageService => services.GetRequiredService<IStorageService>();

    [RelayCommand]
    public void NotificationTest()
    {
        NotificationService.Show("成功", "Toast 訊息測試!", "Success");
    }
    [RelayCommand]
    private async Task OpenFile()
    {
        var filePath = await StorageService.OpenFilePickerAsync("請選擇您的檔案");
        if (!string.IsNullOrEmpty(filePath))
        {
            System.Diagnostics.Debug.WriteLine($"選到的檔案: {filePath}");
        }
    }
    [RelayCommand]
    private async Task OpenFolder()
    {
        var folderPath = await StorageService.OpenFolderPickerAsync();
        if (!string.IsNullOrEmpty(folderPath))
        {
            System.Diagnostics.Debug.WriteLine($"選到的資料夾: {folderPath}");
        }
    }
}
xml
//HomeView.axaml
<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="clr-namespace:AvaloniaSideBarEx.ViewModels"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:DataType="vm:HomeViewModel"
             x:Class="AvaloniaSideBarEx.Views.HomeView">
    <StackPanel Margin="20" Spacing="15">
        <TextBlock Text="{Binding WelcomeMessage}" FontSize="16" Foreground="#34495e"/>
        <Button Command="{Binding  NotificationTest}">Toast</Button>
        <Button Content="開啟檔案"
                Command="{Binding OpenFileCommand}"/>
        <Button Content="開啟資料夾"
                Command="{Binding OpenFolderCommand}"/>
    </StackPanel>
</UserControl>

實際效果

# 檔案與資料夾選擇功能.png

未經授權請勿進行全文複製或商業用途。
Content licensed under CC BY-NC-ND 4.0.