xaml-csharp-development-skill-for-avalonia

Storage Provider and File Picker Workflows

Table of Contents

  1. Scope and APIs
  2. Capability and Lifecycle Model
  3. Open/Save/Folder Picker Patterns
  4. Bookmarks and Path Strategies
  5. XAML-First and Code-Only Usage
  6. Best Practices
  7. Troubleshooting

Scope and APIs

Primary APIs:

Important members:

Reference source files:

Capability and Lifecycle Model

Provider model:

  1. Resolve TopLevel from a visual at interaction time.
  2. Use TopLevel.StorageProvider.
  3. Guard with capability flags before invoking dialogs.
  4. Dispose storage items when not needed.

Notes:

Open/Save/Folder Picker Patterns

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;

public static class StorageWorkflows
{
    public static async Task<IReadOnlyList<IStorageFile>> OpenJsonFilesAsync(Control anchor)
    {
        TopLevel? top = TopLevel.GetTopLevel(anchor);
        if (top is null || !top.StorageProvider.CanOpen)
            return Array.Empty<IStorageFile>();

        IStorageFolder? start = await top.StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents);

        FilePickerFileType json = new("JSON")
        {
            Patterns = new[] { "*.json" },
            MimeTypes = new[] { "application/json" }
        };

        return await top.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
        {
            Title = "Open JSON",
            AllowMultiple = true,
            SuggestedStartLocation = start,
            FileTypeFilter = new[] { json },
            SuggestedFileType = json
        });
    }

    public static async Task<IStorageFile?> SaveTextFileAsync(Control anchor, string suggestedName)
    {
        TopLevel? top = TopLevel.GetTopLevel(anchor);
        if (top is null || !top.StorageProvider.CanSave)
            return null;

        FilePickerFileType text = new("Text")
        {
            Patterns = new[] { "*.txt" },
            MimeTypes = new[] { "text/plain" }
        };

        return await top.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
        {
            Title = "Save As",
            SuggestedFileName = suggestedName,
            DefaultExtension = "txt",
            FileTypeChoices = new[] { text },
            SuggestedFileType = text,
            ShowOverwritePrompt = true
        });
    }
}

Managed Dialog Extensions

When Avalonia.Dialogs managed dialogs are enabled, ManagedFileDialogExtensions adds:

Use these as compatibility shims when you need managed dialog behavior across platforms with inconsistent native dialog support.

Bookmarks and Path Strategies

Bookmark strategy:

Path strategy:

XAML-First and Code-Only Usage

Default mode:

XAML-first complete example:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:MyApp.ViewModels"
             x:DataType="vm:StorageToolsViewModel">
  <StackPanel Margin="12" Spacing="8">
    <Button Content="Open JSON" Command="{CompiledBinding OpenJsonCommand}" />
    <Button Content="Save Report" Command="{CompiledBinding SaveReportCommand}" />
    <TextBlock Text="{CompiledBinding LastPath}" />
  </StackPanel>
</UserControl>

Code-only alternative (on request):

using System.IO;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;

public static class CodeOnlyStorageSample
{
    public static async Task SaveSampleAsync(Control anchor)
    {
        IStorageFile? file = await StorageWorkflows.SaveTextFileAsync(anchor, "report.txt");
        if (file is null)
            return;

        await using Stream stream = await file.OpenWriteAsync();
        await using StreamWriter writer = new(stream, Encoding.UTF8, leaveOpen: false);
        await writer.WriteLineAsync("Generated by Avalonia storage workflow.");
    }
}

Best Practices

Troubleshooting

  1. Empty picker result:
    • User canceled dialog.
    • Capability flag is false on current platform.
  2. Path-dependent code breaks on mobile/browser:
    • Path may be URI-like and not a local file path.
    • Use storage item streams and bookmarks.
  3. Save file returns null:
    • User canceled.
    • Platform save operation unsupported.
  4. Folder picker unavailable:
    • CanPickFolder false for current runtime/backend.