Skip to content

FilePicker: Allow multiple file types using built-in static instances #32123

@matt-goldman

Description

@matt-goldman

Description

The PickOptions type used by FilePicker has a property called FileTypes. The name seems to suggest a list or collection, but it's actually a single value of type FilePickerFileType.

Multiple file types are supported - Internally, this uses a dictionary that maps different platform file type identifiers (MIME types, extensions, etc) to each platform, so that the correct approach is used. FilePickerFileType has a constructor that allows you to pass a dictionary and specify multiple file types this way.

However, this is a little laborious, and requires knowing precisely what the different file type expressions are for each platform.

Very handily, platform implementation methods are provided for common file types and exposed as static instances. For example: PlatformImageFileType, which has implementations for each platform, and results in an instance of FilePickerFileType that has a complete dictionary.

The problem is, as PickerOption only supports one value, you can't use these extremely helpful static instances to build up multiple values. And because the dictionary is private, you can't pull them out to construct your own version.

It would be awesome if we could use these to allow multiple file types easily.

Public API Changes

There are a few ways you could do this.

  1. (Not recommended) Make the dictionary public. This would allow us to map values from the platform file type dictionaries into a custom, more extensive dictionary.

readonly IDictionary<DevicePlatform, IEnumerable<string>> fileTypes;

becomes:

public readonly IDictionary<DevicePlatform, IEnumerable<string>> fileTypes;

see: https://github.com/dotnet/maui/blob/72f23543ccbf1c01e72b20b5204af9a6ea094ff9/src/Essentials/src/FilePicker/FilePicker.shared.cs#L134C3-L134C71

This breaks encapsulation so I wouldn't do this. With that said it may enable other use cases, so may be worth considering, but not for this (IMO).

  1. Add a constructor that takes a collection of FilePickerFileType. This would also require modifying the fileTypes dictionary, it would have to be made internal or protected plus potentially other changes. Then this constructor could map all the provided types to the dictionary at construction time.
public FilePickerFileType(IEnumerable<FilePickerFileType> AcceptedTypes)
{
    fileTypes[DevicePlatform.iOS] = AcceptedTypes.SelectMany(t => t.Mappings[DevicePlatform.iOS]);
    fileTypes[DevicePlatform.Android] = AcceptedTypes.SelectMany(t => t.Mappings[DevicePlatform.Android]);
    fileTypes[DevicePlatform.WinUI] = AcceptedTypes.SelectMany(t => t.Mappings[DevicePlatform.WinUI]);

    // etc.
}
  1. Make the get platform types public.

e.g.:

public partial class FilePickerFileType
	{
		static FilePickerFileType PlatformImageFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImageAll } },
			});

		static FilePickerFileType PlatformPngFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImagePng } }
			});

		static FilePickerFileType PlatformJpegFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImageJpg } }
			});

		static FilePickerFileType PlatformVideoFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.VideoAll } }
			});

		static FilePickerFileType PlatformPdfFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.Pdf } }
			});
	}

becomes:

public partial class FilePickerFileType
	{
		public static FilePickerFileType PlatformImageFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImageAll } },
			});

		public static FilePickerFileType PlatformPngFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImagePng } }
			});

		public static FilePickerFileType PlatformJpegFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.ImageJpg } }
			});

		public static FilePickerFileType PlatformVideoFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.VideoAll } }
			});

		public static FilePickerFileType PlatformPdfFileType() =>
			new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
			{
				{ DevicePlatform.Tizen, new[] { FileMimeTypes.Pdf } }
			});
	}

Intended Use-Case

In my app I have a media picker and file picker. Users can select multiple different types, but none of them are custom, they are all standard types supported by the static instances. I'd love to be able to specify all the supported types without having to create a dictionary and replicate everything that's already there.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions