Search code examples
c#maui.net-6.0maui-blazor

Folder Picker .NET MAUI


I want to code a downloader app that saves pictures to a folder. The app should work on windows and mac/os, and maybe later on android and ios.

I haven't found a way to pick the target folder. Any idea on how it can be achieve either with blazor or xaml .NET MAUI app?


Solution

  • I've made a start implementing this for Windows and macOS. You can review the code here: https://github.com/jfversluis/MauiFolderPickerSample and wrote a little blog post about this as well here: https://blog.verslu.is/maui/folder-picker-with-dotnet-maui/

    This follows kind of the basic pattern you'd want to use if you want to access platform-specific APIs:

    • Define an interface
    • Implement interface on each supported platform
    • Consume functionality

    For this I have created a very simple but effective interface

    public interface IFolderPicker
    {
        Task<string> PickFolder();
    }
    

    Then we create an implementation for Windows, by adding a new file FilePicker.cs to the Platforms\Windows\ folder. This makes it specific to Windows and allows us to write Windows specific code. The file contains this code:

    using WindowsFolderPicker = Windows.Storage.Pickers.FolderPicker;
    
    namespace MauiFolderPickerSample.Platforms.Windows
    {
        public class FolderPicker : IFolderPicker
        {
            public async Task<string> PickFolder()
            {
                var folderPicker = new WindowsFolderPicker();
                // Make it work for Windows 10
                folderPicker.FileTypeFilter.Add("*");
                // Get the current window's HWND by passing in the Window object
                var hwnd = ((MauiWinUIWindow)App.Current.Windows[0].Handler.PlatformView).WindowHandle;
    
                // Associate the HWND with the file picker
                WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hwnd);
    
                var result = await folderPicker.PickSingleFolderAsync();
    
                return result.Path;
            }
        }
    }
    

    Because I chose FolderPicker as the name for my own object here, there is a naming conflict with the Windows FolderPicker that is why there is that weird using at the top. If you go for MyFolderPicker as your object name that wouldn't be needed.

    Now we register this interface and implementation with the generic host builder in our MauiProgram.cs:

    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                });
    
    // Note: this part was added
    
    #if WINDOWS
            builder.Services.AddTransient<IFolderPicker, Platforms.Windows.FolderPicker>();
    #elif MACCATALYST
            builder.Services.AddTransient<IFolderPicker, Platforms.MacCatalyst.FolderPicker>();
    #endif
            builder.Services.AddTransient<MainPage>();
            builder.Services.AddTransient<App>();
    // Note: end added part
    
            return builder.Build();
        }
    }
    

    Note that I also added MainPage and App here so that our constructor injection works (have a look at MainPage.xaml.cs in the linked repository).

    Now we can consume our functionality as follows:

    namespace MauiFolderPickerSample;
    
    public partial class MainPage : ContentPage
    {
        private readonly IFolderPicker _folderPicker;
    
        public MainPage(IFolderPicker folderPicker)
        {
            InitializeComponent();
            _folderPicker = folderPicker;
        }
    
        private async void OnPickFolderClicked(object sender, EventArgs e)
        {
            var pickedFolder = await _folderPicker.PickFolder();
    
            FolderLabel.Text = pickedFolder;
    
            SemanticScreenReader.Announce(FolderLabel.Text);
        }
    }
    

    Implementing other platforms would require you to implement the interface for the platform you want to support and register it in the generic host builder. This should get you started for Windows and macOS.

    Actually calling this should not be any different between .NET MAUI (regular) or .NET MAUI Blazor.