Implement Folder Picker with .NET MAUI, WinUI and macOS

As with any good piece of content, this started as a Stack Overflow question: how to implement a folder picker with .NET MAUI? In this post I’ll show you just that by using dependency injection and implementing platform code.

This post talks about dependency injection. Are you not 100% sure what that is or how it works? Make sure to watch this video I created about it!

Dependency Injection for Absolute Beginners with C# and .NET

Sample Code Notes

For this solution I implemented only Windows and macOS. If you want to implement the rest, you totally can, but I limited myself to these two. In fact, since I am using Mac Catalyst, the implementation should also work on iOS.

Furthermore, the code probably isn’t perfect. It could use error-handling and I’ll admit that I copied the macOS implementation mostly from the FilePicker in .NET MAUI Essentials and adapted it to pick folders.

While the code and sample project is shown in a regular .NET MAUI project, this code should also work with .NET MAUI Blazor and a .NET MAUI Shell project.

This blog post will show the Windows implementation. The full solution can be found on my GitHub repository with the full code for this: https://github.com/jfversluis/MauiFolderPickerSample

Implement Folder Picker with .NET MAUI

The solution I implemented to answer this question follows the basic pattern you’d want to use if you want to access platform-specific APIs in .NET MAUI:

  1. Define an interface
  2. Implement interface on each supported platform
  3. Register implementation with the generic host builder/dependency injection container
  4. Consume functionality

There are more ways to do this, also depending on your specific use-case and preference, but for this solution I thought this made sense. For the other ways, please refer to this Microsoft Docs page that described it.

Define IFolderPicker Interface

First step: implement our interface, that one is pretty straight-forward. You can see it below.

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

With this interface in place, we want to add our implementation for each platform that we want to support.

Implement IFolderPicker Interface for Windows

As mentioned, this post will only focus on the Windows implementation. If you want to support other platforms as well, make sure to repeat this, but with the other platforms’ specific bits.

Create a new file under Platforms\Windows. The logical name would be FolderPicker.cs but of course it can be anything you like. You can see the code for this below.

using WindowsFolderPicker = Windows.Storage.Pickers.FolderPicker;
namespace MauiFolderPickerSample.Platforms.Windows
{
public class FolderPicker : IFolderPicker
{
public async Task<string> PickFolder()
{
var folderPicker = new WindowsFolderPicker();
// 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;
}
}
}
view raw FilePicker.cs hosted with ❤ by GitHub

There is two things to note. First, the weird using at the top. Because of the naming conflict with FolderPicker with the Windows object and my custom object, I had to implement this alias so that the compiler knows which one I’m referring to. If you have named your folder picker differently, this might not be necessary.

Secondly, for these type of things (showing OS dialogs) on Windows we need to have the window handle. Luckily .NET MAUI has an easy way of retrieving that. So we call the Windows FolderPicker, get the selected folder path and return that.

Register Folder Picker Implementation

We’re almost ready to show off our folder picker, but first we need to register the implementation to the interface so it can be resolved at runtime. .NET MAUI has everything onboard to work with dependency injection. Go into your MauiProgram.cs and add the registration for our FolderPicker here. You can see the full code of my MauiProgram below. I have highlighted the added lines with comments.

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();
}
}
view raw MauiProgram.cs hosted with ❤ by GitHub

In the code above, you can see that I also added MainPage and App to my dependency injection container. This way my constructor injection works perfectly throughout the whole application. While it’s not shown here, have a look at MainPage and inside the App.xaml.cs file for the changes I made to wire everything up.

Consume the Folder Picker Functionality

I love it when a plan comes together! This is the final part, let’s call our new functionality. I adapted the file > new .NET MAUI app a bit to only have a label that will show the picked folder path and a button that opens the folder picker. You can see a screenshot below.

Screenshot of the running sample app with the folder picker dialog open

For the event handler of the button I have implemented the code below.

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);
}
}

Starting from the top you can see that I inject the IFolderPicker interface into the constructor. Then in the OnPickFolderClicked handler I call the PickFolder method and get the resulting string from that.

In Closing

In this post we have seen how to implement a folder picker with .NET MAUI. We have done so by leveraging dependency injection and with that writing platform code to achieve a working folder picker.

All the sample code can be found on my GitHub page, here: https://github.com/jfversluis/MauiFolderPickerSample

1 thought on “Implement Folder Picker with .NET MAUI, WinUI and macOS”

Comments are closed.