maui
maui copied to clipboard
[.NET 10] Implement multi-select for MediaPicker
[!NOTE] Are you waiting for the changes in this PR to be merged? It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
This PR adds the ability to pick multiple files at once through MediaPicker. The single pick methods are obsoleted in favor of the multi pick ones. This prevents the need to have a separate options objects or parameter etc. Now the MediaPickerOptions object has a new field SelectionLimit with a default value of 1. So default is still a single pick action. Then 0 means unlimited and any other number is the actual limit.
There are a few notes:
- iOS, works as expected, just set the limit and the UI will block the user from picking more than configured
- Android, the default UI blocks the user, but on Android people have the option to use their own custom picker. Those pickers are not guaranteed to honor the maximum selection limit, so be aware.
- Windows, does not have any selection limit implemented at all. This is not available through the Windows APIs. Make sure to implement your own code to detect this.
Issues Fixed
Fixes #29079
Maybe this is the subject of future work, but how do you feel about simplifying the API even further and having just a single PickAsync method and moving the media type specification to the options. This would allow the selection of both images and videos (provided each platform supports it).
Something like:
Definition
/// <summary>
/// Represents the type of media that can be picked using the MediaPicker API.
/// </summary>
public enum PickableMediaType
{
/// <summary>
/// Represents all types of media that can be selected.
/// </summary>
All = 0,
/// <summary>
/// Represents image media that can be selected.
/// </summary>
Image = 1,
/// <summary>
/// Represents video media that can be selected.
/// </summary>
Video = 2,
}
/// <summary>
/// Pick options for picking media from the device.
/// </summary>
public class MediaPickerOptions
{
/// <summary>
/// Gets or sets the type of media that can be picked.
/// Default value is <see cref="PickableMediaType.All"/>.
/// </summary>
public PickableMediaType PickableMediaType { get; set; } = PickableMediaType.All;
/// <summary>
/// Gets or sets the title that is displayed when picking media.
/// </summary>
/// <remarks>This title is not guaranteed to be shown on all operating systems.</remarks>
public string? Title { get; set; }
/// <summary>
/// Gets or sets the maximum number of items that can be selected. Default value is 1.
/// </summary>
/// <remarks>
/// A value of 0 means no limit.
/// </remarks>
public int SelectionLimit { get; set; } = 1;
}
/// <summary>
/// The MediaPicker API lets a user pick or take a photo or video on the device.
/// </summary>
public interface IMediaPicker
{
/// <summary>
/// Gets a value indicating whether capturing media is supported on this device.
/// </summary>
bool IsCaptureSupported { get; }
// NEW
Task<IEnumerable<FileResult>> PickAsync(MediaPickerOptions? options = null);
/// <summary>
/// Opens the media browser to select a photo.
/// </summary>
/// <param name="options">Pick options to use.</param>
/// <returns>A <see cref="FileResult"/> object containing details of the picked photo. When the operation was cancelled by the user, this will return <see langword="null"/>.</returns>
/// <remarks>When using <see cref="MediaPickerOptions.SelectionLimit"/> on this overload, it will <b>not</b> have effect.</remarks>
[Obsolete($"Use {nameof(PickAsync)} instead.")]
Task<FileResult?> PickPhotoAsync(MediaPickerOptions? options = null);
/// <summary>
/// Opens the camera to take a photo.
/// </summary>
/// <param name="options">Pick options to use.</param>
/// <returns>A <see cref="FileResult"/> object containing details of the captured photo. When the operation was cancelled by the user, this will return <see langword="null"/>.</returns>
Task<FileResult?> CapturePhotoAsync(MediaPickerOptions? options = null);
/// <summary>
/// Opens the media browser to select a video.
/// </summary>
/// <param name="options">Pick options to use.</param>
/// <returns>A <see cref="FileResult"/> object containing details of the picked video. When the operation was cancelled by the user, this will return <see langword="null"/>.</returns>
/// <remarks>When using <see cref="MediaPickerOptions.SelectionLimit"/> on this overload, it will <b>not</b> have effect.</remarks>
[Obsolete($"Use {nameof(PickAsync)} instead.")]
Task<FileResult?> PickVideoAsync(MediaPickerOptions? options = null);
/// <summary>
/// Opens the camera to take a video.
/// </summary>
/// <param name="options">Pick options to use.</param>
/// <returns>A <see cref="FileResult"/> object containing details of the captured video. When the operation was cancelled by the user, this will return <see langword="null"/>.</returns>
Task<FileResult?> CaptureVideoAsync(MediaPickerOptions? options = null);
}
Usage
IEnumerable<FileResult> singleImage = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick an image",
SelectionLimit = 1,
PickableMediaType = PickableMediaType.Image
});
IEnumerable<FileResult> images = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick images",
SelectionLimit = 5,
PickableMediaType = PickableMediaType.Image
});
IEnumerable<FileResult> singleVideo = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick a video",
SelectionLimit = 1,
PickableMediaType = PickableMediaType.Video
});
IEnumerable<FileResult> videos = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick videos",
SelectionLimit = 5,
PickableMediaType = PickableMediaType.Video
});
IEnumerable<FileResult> media = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick image or video",
SelectionLimit = 1,
PickableMediaType = PickableMediaType.All
});
IEnumerable<FileResult> medias = await MediaPicker.PickAsync(new MediaPickerOptions
{
Title = "Pick images and/or videos",
SelectionLimit = 5,
PickableMediaType = PickableMediaType.All
});
Seems like an easy win, but maybe there's something with FileResult that makes it a bit more complex.
Oh I kinda like that idea... Are we sure this is possible for all platforms? Mainly mixing between images and videos?