Skip to content

Commit 848c5c4

Browse files
Revise MediaPicker migration details and framework updates (#424)
* Revise MediaPicker migration details and framework updates Updated deprecation notes and migration instructions for MediaPicker APIs. Adjusted target framework versions and deprecated API lists. * Update TargetFrameworks for .NET MAUI upgrade * Update Windows target framework version in instructions --------- Co-authored-by: Aaron Powell <[email protected]>
1 parent 44ce287 commit 848c5c4

File tree

1 file changed

+327
-1
lines changed

1 file changed

+327
-1
lines changed

instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md

Lines changed: 327 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 b
1717
- [MessagingCenter Made Internal](#messagingcenter-made-internal)
1818
- [ListView and TableView Deprecated](#listview-and-tableview-deprecated)
1919
4. [Deprecated APIs (P1 - Fix Soon)](#deprecated-apis-p1---fix-soon)
20+
- [Animation Methods](#1-animation-methods)
21+
- [DisplayAlert and DisplayActionSheet](#2-displayalert-and-displayactionsheet)
22+
- [Page.IsBusy](#3-pageisbusy)
23+
- [MediaPicker APIs](#4-mediapicker-apis)
2024
5. [Recommended Changes (P2)](#recommended-changes-p2)
2125
6. [Bulk Migration Tools](#bulk-migration-tools)
2226
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
3236
2. **Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED
3337
3. **Fix breaking changes** - MessagingCenter (P0)
3438
4. **Migrate ListView/TableView to CollectionView** (P0 - CRITICAL)
35-
5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy (P1)
39+
5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy, MediaPicker (P1)
3640

3741
> ⚠️ **Major Breaking Changes**:
3842
> - CommunityToolkit.Maui **must** be version 12.3.0 or later
@@ -1087,6 +1091,253 @@ public class MyViewModel : INotifyPropertyChanged
10871091

10881092
---
10891093

1094+
### 4. MediaPicker APIs
1095+
1096+
**Status:** ⚠️ **DEPRECATED** - Single-selection methods replaced with multi-selection variants.
1097+
1098+
**Warning You'll See:**
1099+
```
1100+
warning CS0618: 'MediaPicker.PickPhotoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickPhotosAsync which also allows multiple selections.'
1101+
warning CS0618: 'MediaPicker.PickVideoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickVideosAsync which also allows multiple selections.'
1102+
```
1103+
1104+
**What Changed:**
1105+
- `PickPhotoAsync()``PickPhotosAsync()` (returns `List<FileResult>`)
1106+
- `PickVideoAsync()``PickVideosAsync()` (returns `List<FileResult>`)
1107+
- New `SelectionLimit` property on `MediaPickerOptions` (default: 1)
1108+
- Old methods still work but are marked obsolete
1109+
1110+
**Key Behavior:**
1111+
- **Default behavior preserved:** `SelectionLimit = 1` (single selection)
1112+
- Set `SelectionLimit = 0` for unlimited multi-select
1113+
- Set `SelectionLimit > 1` for specific limits
1114+
1115+
**Platform Notes:**
1116+
-**iOS:** Selection limit enforced by native picker UI
1117+
- ⚠️ **Android:** Not all custom pickers honor `SelectionLimit` - be aware!
1118+
- ⚠️ **Windows:** `SelectionLimit` not supported - implement your own validation
1119+
1120+
#### Migration Examples
1121+
1122+
**Simple Photo Picker (maintain single-selection behavior):**
1123+
```csharp
1124+
// ❌ OLD (Deprecated)
1125+
var photo = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
1126+
{
1127+
Title = "Pick a photo"
1128+
});
1129+
1130+
if (photo != null)
1131+
{
1132+
var stream = await photo.OpenReadAsync();
1133+
MyImage.Source = ImageSource.FromStream(() => stream);
1134+
}
1135+
1136+
// ✅ NEW (maintains same behavior - picks only 1 photo)
1137+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1138+
{
1139+
Title = "Pick a photo",
1140+
SelectionLimit = 1 // Explicit: only 1 photo
1141+
});
1142+
1143+
var photo = photos.FirstOrDefault();
1144+
if (photo != null)
1145+
{
1146+
var stream = await photo.OpenReadAsync();
1147+
MyImage.Source = ImageSource.FromStream(() => stream);
1148+
}
1149+
```
1150+
1151+
**Simple Video Picker (maintain single-selection behavior):**
1152+
```csharp
1153+
// ❌ OLD (Deprecated)
1154+
var video = await MediaPicker.PickVideoAsync(new MediaPickerOptions
1155+
{
1156+
Title = "Pick a video"
1157+
});
1158+
1159+
if (video != null)
1160+
{
1161+
VideoPlayer.Source = video.FullPath;
1162+
}
1163+
1164+
// ✅ NEW (maintains same behavior - picks only 1 video)
1165+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
1166+
{
1167+
Title = "Pick a video",
1168+
SelectionLimit = 1 // Explicit: only 1 video
1169+
});
1170+
1171+
var video = videos.FirstOrDefault();
1172+
if (video != null)
1173+
{
1174+
VideoPlayer.Source = video.FullPath;
1175+
}
1176+
```
1177+
1178+
**Photo Picker without Options (uses defaults):**
1179+
```csharp
1180+
// ❌ OLD (Deprecated)
1181+
var photo = await MediaPicker.PickPhotoAsync();
1182+
1183+
// ✅ NEW (default SelectionLimit = 1, so same behavior)
1184+
var photos = await MediaPicker.PickPhotosAsync();
1185+
var photo = photos.FirstOrDefault();
1186+
```
1187+
1188+
**Multi-Photo Selection (new capability):**
1189+
```csharp
1190+
// ✅ NEW: Pick up to 5 photos
1191+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1192+
{
1193+
Title = "Pick up to 5 photos",
1194+
SelectionLimit = 5
1195+
});
1196+
1197+
foreach (var photo in photos)
1198+
{
1199+
var stream = await photo.OpenReadAsync();
1200+
// Process each photo
1201+
}
1202+
1203+
// ✅ NEW: Unlimited selection
1204+
var allPhotos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1205+
{
1206+
Title = "Pick photos",
1207+
SelectionLimit = 0 // No limit
1208+
});
1209+
```
1210+
1211+
**Multi-Video Selection (new capability):**
1212+
```csharp
1213+
// ✅ NEW: Pick up to 3 videos
1214+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
1215+
{
1216+
Title = "Pick up to 3 videos",
1217+
SelectionLimit = 3
1218+
});
1219+
1220+
foreach (var video in videos)
1221+
{
1222+
// Process each video
1223+
Console.WriteLine($"Selected: {video.FileName}");
1224+
}
1225+
```
1226+
1227+
**Handling Empty Results:**
1228+
```csharp
1229+
// NEW: Returns empty list if user cancels (not null)
1230+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1231+
{
1232+
SelectionLimit = 1
1233+
});
1234+
1235+
// ✅ Check for empty list
1236+
if (photos.Count == 0)
1237+
{
1238+
await DisplayAlertAsync("Cancelled", "No photo selected", "OK");
1239+
return;
1240+
}
1241+
1242+
var photo = photos.First();
1243+
// Process photo...
1244+
```
1245+
1246+
**With Try-Catch (same as before):**
1247+
```csharp
1248+
try
1249+
{
1250+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1251+
{
1252+
Title = "Pick a photo",
1253+
SelectionLimit = 1
1254+
});
1255+
1256+
if (photos.Count > 0)
1257+
{
1258+
await ProcessPhotoAsync(photos.First());
1259+
}
1260+
}
1261+
catch (PermissionException)
1262+
{
1263+
await DisplayAlertAsync("Permission Denied", "Camera access required", "OK");
1264+
}
1265+
catch (Exception ex)
1266+
{
1267+
await DisplayAlertAsync("Error", $"Failed to pick photo: {ex.Message}", "OK");
1268+
}
1269+
```
1270+
1271+
#### Migration Checklist
1272+
1273+
When migrating to the new MediaPicker APIs:
1274+
1275+
- [ ] Replace `PickPhotoAsync()` with `PickPhotosAsync()`
1276+
- [ ] Replace `PickVideoAsync()` with `PickVideosAsync()`
1277+
- [ ] Set `SelectionLimit = 1` to maintain single-selection behavior
1278+
- [ ] Change `FileResult?` to `List<FileResult>` (or use `.FirstOrDefault()`)
1279+
- [ ] Update null checks to empty list checks (`photos.Count == 0`)
1280+
- [ ] Test on Android - ensure custom pickers respect limit (or add validation)
1281+
- [ ] Test on Windows - add your own limit validation if needed
1282+
- [ ] Consider if multi-select would improve your UX (optional)
1283+
1284+
#### Platform-Specific Validation (Windows & Android)
1285+
1286+
```csharp
1287+
// ✅ Recommended: Validate selection limit on platforms that don't enforce it
1288+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1289+
{
1290+
Title = "Pick up to 5 photos",
1291+
SelectionLimit = 5
1292+
});
1293+
1294+
// On Windows and some Android pickers, the limit might not be enforced
1295+
if (photos.Count > 5)
1296+
{
1297+
await DisplayAlertAsync(
1298+
"Too Many Photos",
1299+
$"Please select up to 5 photos. You selected {photos.Count}.",
1300+
"OK"
1301+
);
1302+
return;
1303+
}
1304+
1305+
// Continue processing...
1306+
```
1307+
1308+
#### Capture Methods (unchanged)
1309+
1310+
**Note:** Capture methods (`CapturePhotoAsync`, `CaptureVideoAsync`) are **NOT** deprecated and remain unchanged:
1311+
1312+
```csharp
1313+
// ✅ These still work as-is (no changes needed)
1314+
var photo = await MediaPicker.CapturePhotoAsync();
1315+
var video = await MediaPicker.CaptureVideoAsync();
1316+
```
1317+
1318+
#### Quick Migration Pattern
1319+
1320+
**For all existing single-selection code, use this pattern:**
1321+
1322+
```csharp
1323+
// ❌ OLD
1324+
var photo = await MediaPicker.PickPhotoAsync(options);
1325+
if (photo != null)
1326+
{
1327+
// Process photo
1328+
}
1329+
1330+
// ✅ NEW (drop-in replacement)
1331+
var photos = await MediaPicker.PickPhotosAsync(options ?? new MediaPickerOptions { SelectionLimit = 1 });
1332+
var photo = photos.FirstOrDefault();
1333+
if (photo != null)
1334+
{
1335+
// Process photo (same code as before)
1336+
}
1337+
```
1338+
1339+
---
1340+
10901341
## Recommended Changes (P2)
10911342

10921343
These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle.
@@ -1203,6 +1454,31 @@ Find: DisplayActionSheet\(
12031454
Replace: DisplayActionSheetAsync(
12041455
```
12051456

1457+
#### MediaPicker Methods
1458+
1459+
**⚠️ Note:** MediaPicker migration requires manual code changes due to return type changes (`FileResult?``List<FileResult>`). Use these searches to find instances:
1460+
1461+
```bash
1462+
# Find PickPhotoAsync usages
1463+
grep -rn "PickPhotoAsync" --include="*.cs" .
1464+
1465+
# Find PickVideoAsync usages
1466+
grep -rn "PickVideoAsync" --include="*.cs" .
1467+
```
1468+
1469+
**Manual Migration Pattern:**
1470+
```csharp
1471+
// Find: await MediaPicker.PickPhotoAsync(
1472+
// Replace with:
1473+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions { SelectionLimit = 1 });
1474+
var photo = photos.FirstOrDefault();
1475+
1476+
// Find: await MediaPicker.PickVideoAsync(
1477+
// Replace with:
1478+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions { SelectionLimit = 1 });
1479+
var video = videos.FirstOrDefault();
1480+
```
1481+
12061482
#### ListView/TableView Detection (Manual Migration Required)
12071483

12081484
**⚠️ Note:** ListView/TableView migration CANNOT be automated. Use these searches to find instances:
@@ -1416,6 +1692,54 @@ dotnet workload update
14161692

14171693
---
14181694

1695+
### Warning: MediaPicker methods are obsolete
1696+
1697+
**Cause:** Using deprecated `PickPhotoAsync` or `PickVideoAsync` methods.
1698+
1699+
**Solution:** Migrate to `PickPhotosAsync` or `PickVideosAsync`:
1700+
1701+
```csharp
1702+
// ❌ OLD
1703+
var photo = await MediaPicker.PickPhotoAsync(options);
1704+
1705+
// ✅ NEW (maintain single-selection)
1706+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1707+
{
1708+
Title = options?.Title,
1709+
SelectionLimit = 1
1710+
});
1711+
var photo = photos.FirstOrDefault();
1712+
```
1713+
1714+
**Key Changes:**
1715+
- Return type changes from `FileResult?` to `List<FileResult>`
1716+
- Use `.FirstOrDefault()` to get single result
1717+
- Set `SelectionLimit = 1` to maintain old behavior
1718+
- Check `photos.Count == 0` instead of `photo == null`
1719+
1720+
---
1721+
1722+
### MediaPicker returns more items than SelectionLimit
1723+
1724+
**Cause:** Windows and some Android custom pickers don't enforce `SelectionLimit`.
1725+
1726+
**Solution:** Add manual validation:
1727+
1728+
```csharp
1729+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
1730+
{
1731+
SelectionLimit = 5
1732+
});
1733+
1734+
if (photos.Count > 5)
1735+
{
1736+
await DisplayAlertAsync("Error", "Too many photos selected", "OK");
1737+
return;
1738+
}
1739+
```
1740+
1741+
---
1742+
14191743
### Animation doesn't complete after migration
14201744

14211745
**Cause:** Forgetting `await` keyword.
@@ -1556,6 +1880,8 @@ warning CS0618: 'ListView' is obsolete: 'With the deprecation of ListView, this
15561880
- [ ] Update `DisplayAlert``DisplayAlertAsync`
15571881
- [ ] Update `DisplayActionSheet``DisplayActionSheetAsync`
15581882
- [ ] Replace `Page.IsBusy` with `ActivityIndicator`
1883+
- [ ] Replace `PickPhotoAsync``PickPhotosAsync` (with `SelectionLimit = 1`)
1884+
- [ ] Replace `PickVideoAsync``PickVideosAsync` (with `SelectionLimit = 1`)
15591885

15601886
**Nice to Have (P2):**
15611887
- [ ] Migrate `Application.MainPage` to `CreateWindow`

0 commit comments

Comments
 (0)