Revise MediaPicker migration details and framework updates

Updated deprecation notes and migration instructions for MediaPicker APIs. Adjusted target framework versions and deprecated API lists.
This commit is contained in:
Gerald Versluis 2025-11-19 08:59:23 +01:00 committed by GitHub
parent bd2bba116d
commit d290c8a398
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -17,6 +17,10 @@ This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 b
- [MessagingCenter Made Internal](#messagingcenter-made-internal) - [MessagingCenter Made Internal](#messagingcenter-made-internal)
- [ListView and TableView Deprecated](#listview-and-tableview-deprecated) - [ListView and TableView Deprecated](#listview-and-tableview-deprecated)
4. [Deprecated APIs (P1 - Fix Soon)](#deprecated-apis-p1---fix-soon) 4. [Deprecated APIs (P1 - Fix Soon)](#deprecated-apis-p1---fix-soon)
- [Animation Methods](#1-animation-methods)
- [DisplayAlert and DisplayActionSheet](#2-displayalert-and-displayactionsheet)
- [Page.IsBusy](#3-pageisbusy)
- [MediaPicker APIs](#4-mediapicker-apis)
5. [Recommended Changes (P2)](#recommended-changes-p2) 5. [Recommended Changes (P2)](#recommended-changes-p2)
6. [Bulk Migration Tools](#bulk-migration-tools) 6. [Bulk Migration Tools](#bulk-migration-tools)
7. [Testing Your Upgrade](#testing-your-upgrade) 7. [Testing Your Upgrade](#testing-your-upgrade)
@ -32,7 +36,7 @@ This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 b
2. **Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED 2. **Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED
3. **Fix breaking changes** - MessagingCenter (P0) 3. **Fix breaking changes** - MessagingCenter (P0)
4. **Migrate ListView/TableView to CollectionView** (P0 - CRITICAL) 4. **Migrate ListView/TableView to CollectionView** (P0 - CRITICAL)
5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy (P1) 5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy, MediaPicker (P1)
> ⚠️ **Major Breaking Changes**: > ⚠️ **Major Breaking Changes**:
> - CommunityToolkit.Maui **must** be version 12.3.0 or later > - CommunityToolkit.Maui **must** be version 12.3.0 or later
@ -57,7 +61,7 @@ This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 b
```xml ```xml
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.22621.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
``` ```
@ -76,7 +80,7 @@ This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 b
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks> <TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<!-- Add Windows only when on Windows --> <!-- Add Windows only when on Windows -->
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.22621.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
``` ```
@ -1087,6 +1091,253 @@ public class MyViewModel : INotifyPropertyChanged
--- ---
### 4. MediaPicker APIs
**Status:** ⚠️ **DEPRECATED** - Single-selection methods replaced with multi-selection variants.
**Warning You'll See:**
```
warning CS0618: 'MediaPicker.PickPhotoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickPhotosAsync which also allows multiple selections.'
warning CS0618: 'MediaPicker.PickVideoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickVideosAsync which also allows multiple selections.'
```
**What Changed:**
- `PickPhotoAsync()``PickPhotosAsync()` (returns `List<FileResult>`)
- `PickVideoAsync()``PickVideosAsync()` (returns `List<FileResult>`)
- New `SelectionLimit` property on `MediaPickerOptions` (default: 1)
- Old methods still work but are marked obsolete
**Key Behavior:**
- **Default behavior preserved:** `SelectionLimit = 1` (single selection)
- Set `SelectionLimit = 0` for unlimited multi-select
- Set `SelectionLimit > 1` for specific limits
**Platform Notes:**
- ✅ **iOS:** Selection limit enforced by native picker UI
- ⚠️ **Android:** Not all custom pickers honor `SelectionLimit` - be aware!
- ⚠️ **Windows:** `SelectionLimit` not supported - implement your own validation
#### Migration Examples
**Simple Photo Picker (maintain single-selection behavior):**
```csharp
// ❌ OLD (Deprecated)
var photo = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
{
Title = "Pick a photo"
});
if (photo != null)
{
var stream = await photo.OpenReadAsync();
MyImage.Source = ImageSource.FromStream(() => stream);
}
// ✅ NEW (maintains same behavior - picks only 1 photo)
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = "Pick a photo",
SelectionLimit = 1 // Explicit: only 1 photo
});
var photo = photos.FirstOrDefault();
if (photo != null)
{
var stream = await photo.OpenReadAsync();
MyImage.Source = ImageSource.FromStream(() => stream);
}
```
**Simple Video Picker (maintain single-selection behavior):**
```csharp
// ❌ OLD (Deprecated)
var video = await MediaPicker.PickVideoAsync(new MediaPickerOptions
{
Title = "Pick a video"
});
if (video != null)
{
VideoPlayer.Source = video.FullPath;
}
// ✅ NEW (maintains same behavior - picks only 1 video)
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
{
Title = "Pick a video",
SelectionLimit = 1 // Explicit: only 1 video
});
var video = videos.FirstOrDefault();
if (video != null)
{
VideoPlayer.Source = video.FullPath;
}
```
**Photo Picker without Options (uses defaults):**
```csharp
// ❌ OLD (Deprecated)
var photo = await MediaPicker.PickPhotoAsync();
// ✅ NEW (default SelectionLimit = 1, so same behavior)
var photos = await MediaPicker.PickPhotosAsync();
var photo = photos.FirstOrDefault();
```
**Multi-Photo Selection (new capability):**
```csharp
// ✅ NEW: Pick up to 5 photos
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = "Pick up to 5 photos",
SelectionLimit = 5
});
foreach (var photo in photos)
{
var stream = await photo.OpenReadAsync();
// Process each photo
}
// ✅ NEW: Unlimited selection
var allPhotos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = "Pick photos",
SelectionLimit = 0 // No limit
});
```
**Multi-Video Selection (new capability):**
```csharp
// ✅ NEW: Pick up to 3 videos
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
{
Title = "Pick up to 3 videos",
SelectionLimit = 3
});
foreach (var video in videos)
{
// Process each video
Console.WriteLine($"Selected: {video.FileName}");
}
```
**Handling Empty Results:**
```csharp
// NEW: Returns empty list if user cancels (not null)
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
SelectionLimit = 1
});
// ✅ Check for empty list
if (photos.Count == 0)
{
await DisplayAlertAsync("Cancelled", "No photo selected", "OK");
return;
}
var photo = photos.First();
// Process photo...
```
**With Try-Catch (same as before):**
```csharp
try
{
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = "Pick a photo",
SelectionLimit = 1
});
if (photos.Count > 0)
{
await ProcessPhotoAsync(photos.First());
}
}
catch (PermissionException)
{
await DisplayAlertAsync("Permission Denied", "Camera access required", "OK");
}
catch (Exception ex)
{
await DisplayAlertAsync("Error", $"Failed to pick photo: {ex.Message}", "OK");
}
```
#### Migration Checklist
When migrating to the new MediaPicker APIs:
- [ ] Replace `PickPhotoAsync()` with `PickPhotosAsync()`
- [ ] Replace `PickVideoAsync()` with `PickVideosAsync()`
- [ ] Set `SelectionLimit = 1` to maintain single-selection behavior
- [ ] Change `FileResult?` to `List<FileResult>` (or use `.FirstOrDefault()`)
- [ ] Update null checks to empty list checks (`photos.Count == 0`)
- [ ] Test on Android - ensure custom pickers respect limit (or add validation)
- [ ] Test on Windows - add your own limit validation if needed
- [ ] Consider if multi-select would improve your UX (optional)
#### Platform-Specific Validation (Windows & Android)
```csharp
// ✅ Recommended: Validate selection limit on platforms that don't enforce it
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = "Pick up to 5 photos",
SelectionLimit = 5
});
// On Windows and some Android pickers, the limit might not be enforced
if (photos.Count > 5)
{
await DisplayAlertAsync(
"Too Many Photos",
$"Please select up to 5 photos. You selected {photos.Count}.",
"OK"
);
return;
}
// Continue processing...
```
#### Capture Methods (unchanged)
**Note:** Capture methods (`CapturePhotoAsync`, `CaptureVideoAsync`) are **NOT** deprecated and remain unchanged:
```csharp
// ✅ These still work as-is (no changes needed)
var photo = await MediaPicker.CapturePhotoAsync();
var video = await MediaPicker.CaptureVideoAsync();
```
#### Quick Migration Pattern
**For all existing single-selection code, use this pattern:**
```csharp
// ❌ OLD
var photo = await MediaPicker.PickPhotoAsync(options);
if (photo != null)
{
// Process photo
}
// ✅ NEW (drop-in replacement)
var photos = await MediaPicker.PickPhotosAsync(options ?? new MediaPickerOptions { SelectionLimit = 1 });
var photo = photos.FirstOrDefault();
if (photo != null)
{
// Process photo (same code as before)
}
```
---
## Recommended Changes (P2) ## Recommended Changes (P2)
These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle. These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle.
@ -1203,6 +1454,31 @@ Find: DisplayActionSheet\(
Replace: DisplayActionSheetAsync( Replace: DisplayActionSheetAsync(
``` ```
#### MediaPicker Methods
**⚠️ Note:** MediaPicker migration requires manual code changes due to return type changes (`FileResult?``List<FileResult>`). Use these searches to find instances:
```bash
# Find PickPhotoAsync usages
grep -rn "PickPhotoAsync" --include="*.cs" .
# Find PickVideoAsync usages
grep -rn "PickVideoAsync" --include="*.cs" .
```
**Manual Migration Pattern:**
```csharp
// Find: await MediaPicker.PickPhotoAsync(
// Replace with:
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions { SelectionLimit = 1 });
var photo = photos.FirstOrDefault();
// Find: await MediaPicker.PickVideoAsync(
// Replace with:
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions { SelectionLimit = 1 });
var video = videos.FirstOrDefault();
```
#### ListView/TableView Detection (Manual Migration Required) #### ListView/TableView Detection (Manual Migration Required)
**⚠️ Note:** ListView/TableView migration CANNOT be automated. Use these searches to find instances: **⚠️ Note:** ListView/TableView migration CANNOT be automated. Use these searches to find instances:
@ -1416,6 +1692,54 @@ dotnet workload update
--- ---
### Warning: MediaPicker methods are obsolete
**Cause:** Using deprecated `PickPhotoAsync` or `PickVideoAsync` methods.
**Solution:** Migrate to `PickPhotosAsync` or `PickVideosAsync`:
```csharp
// ❌ OLD
var photo = await MediaPicker.PickPhotoAsync(options);
// ✅ NEW (maintain single-selection)
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
Title = options?.Title,
SelectionLimit = 1
});
var photo = photos.FirstOrDefault();
```
**Key Changes:**
- Return type changes from `FileResult?` to `List<FileResult>`
- Use `.FirstOrDefault()` to get single result
- Set `SelectionLimit = 1` to maintain old behavior
- Check `photos.Count == 0` instead of `photo == null`
---
### MediaPicker returns more items than SelectionLimit
**Cause:** Windows and some Android custom pickers don't enforce `SelectionLimit`.
**Solution:** Add manual validation:
```csharp
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
{
SelectionLimit = 5
});
if (photos.Count > 5)
{
await DisplayAlertAsync("Error", "Too many photos selected", "OK");
return;
}
```
---
### Animation doesn't complete after migration ### Animation doesn't complete after migration
**Cause:** Forgetting `await` keyword. **Cause:** Forgetting `await` keyword.
@ -1556,6 +1880,8 @@ warning CS0618: 'ListView' is obsolete: 'With the deprecation of ListView, this
- [ ] Update `DisplayAlert``DisplayAlertAsync` - [ ] Update `DisplayAlert``DisplayAlertAsync`
- [ ] Update `DisplayActionSheet``DisplayActionSheetAsync` - [ ] Update `DisplayActionSheet``DisplayActionSheetAsync`
- [ ] Replace `Page.IsBusy` with `ActivityIndicator` - [ ] Replace `Page.IsBusy` with `ActivityIndicator`
- [ ] Replace `PickPhotoAsync``PickPhotosAsync` (with `SelectionLimit = 1`)
- [ ] Replace `PickVideoAsync``PickVideosAsync` (with `SelectionLimit = 1`)
**Nice to Have (P2):** **Nice to Have (P2):**
- [ ] Migrate `Application.MainPage` to `CreateWindow` - [ ] Migrate `Application.MainPage` to `CreateWindow`