@@ -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 )
19194 . [ 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 )
20245 . [ Recommended Changes (P2)] ( #recommended-changes-p2 )
21256 . [ Bulk Migration Tools] ( #bulk-migration-tools )
22267 . [ 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
32362 . ** Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED
33373 . ** Fix breaking changes** - MessagingCenter (P0)
34384 . ** 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
10921343These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle.
@@ -1203,6 +1454,31 @@ Find: DisplayActionSheet\(
12031454Replace: 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