diff --git a/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md b/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md new file mode 100644 index 0000000..16a3b62 --- /dev/null +++ b/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md @@ -0,0 +1,1591 @@ +# Upgrading from .NET MAUI 9 to .NET MAUI 10 + +This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 by focusing on the critical breaking changes and obsolete APIs that require code updates. + +--- + +## Table of Contents + +1. [Quick Start](#quick-start) +2. [Update Target Framework](#update-target-framework) +3. [Breaking Changes (P0 - Must Fix)](#breaking-changes-p0---must-fix) + - [MessagingCenter Made Internal](#messagingcenter-made-internal) + - [ListView and TableView Deprecated](#listview-and-tableview-deprecated) +4. [Deprecated APIs (P1 - Fix Soon)](#deprecated-apis-p1---fix-soon) +5. [Recommended Changes (P2)](#recommended-changes-p2) +6. [Bulk Migration Tools](#bulk-migration-tools) +7. [Testing Your Upgrade](#testing-your-upgrade) +8. [Troubleshooting](#troubleshooting) + +--- + +## Quick Start + +**Five-Step Upgrade Process:** + +1. **Update TargetFramework** to `net10.0` +2. **Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED +3. **Fix breaking changes** - MessagingCenter (P0) +4. **Migrate ListView/TableView to CollectionView** (P0 - CRITICAL) +5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy (P1) + +> âš ī¸ **Major Breaking Changes**: +> - CommunityToolkit.Maui **must** be version 12.3.0 or later +> - ListView and TableView are now obsolete (most significant migration effort) + +--- + +## Update Target Framework + +### Single Platform + +```xml + + + net10.0 + + +``` + +### Multi-Platform + +```xml + + + net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.22621.0 + + +``` + +### Optional: Linux Compatibility (GitHub Copilot, WSL, etc.) + +> 💡 **For Linux Development**: If you're building on Linux (e.g., GitHub Codespaces, WSL, or using GitHub Copilot), you can make your project compile on Linux by conditionally excluding iOS/Mac Catalyst targets: + +```xml + + + + net10.0-android + + + $(TargetFrameworks);net10.0-ios;net10.0-maccatalyst + + + $(TargetFrameworks);net10.0-windows10.0.22621.0 + + +``` + +**Benefits:** +- ✅ Compiles successfully on Linux (no iOS/Mac tools required) +- ✅ Works with GitHub Codespaces and Copilot +- ✅ Automatically includes correct targets based on build OS +- ✅ No changes needed when switching between OS environments + +**Reference:** [dotnet/maui#32186](https://github.com/dotnet/maui/pull/32186) + +### Update Required NuGet Packages + +> âš ī¸ **CRITICAL**: If you use CommunityToolkit.Maui, you **must** update to version 12.3.0 or later. Earlier versions are not compatible with .NET 10 and will cause compilation errors. + +```bash +# Update CommunityToolkit.Maui (if you use it) +dotnet add package CommunityToolkit.Maui --version 12.3.0 + +# Update other common packages to .NET 10 compatible versions +dotnet add package Microsoft.Maui.Controls --version 10.0.0 +``` + +**Check all your NuGet packages:** +```bash +# List all packages and check for updates +dotnet list package --outdated + +# Update all packages to latest compatible versions +dotnet list package --outdated | grep ">" | cut -d '>' -f 1 | xargs -I {} dotnet add package {} +``` + +--- + +## Breaking Changes (P0 - Must Fix) + +### MessagingCenter Made Internal + +**Status:** 🚨 **BREAKING** - `MessagingCenter` is now `internal` and cannot be accessed. + +**Error You'll See:** +``` +error CS0122: 'MessagingCenter' is inaccessible due to its protection level +``` + +**Migration Required:** + +#### Step 1: Install CommunityToolkit.Mvvm + +```bash +dotnet add package CommunityToolkit.Mvvm --version 8.3.0 +``` + +#### Step 2: Define Message Classes + +```csharp +// OLD: No message class needed +MessagingCenter.Send(this, "UserLoggedIn", userData); + +// NEW: Create a message class +public class UserLoggedInMessage +{ + public UserData Data { get; set; } + + public UserLoggedInMessage(UserData data) + { + Data = data; + } +} +``` + +#### Step 3: Update Send Calls + +```csharp +// ❌ OLD (Broken in .NET 10) +using Microsoft.Maui.Controls; + +MessagingCenter.Send(this, "UserLoggedIn", userData); +MessagingCenter.Send(this, "StatusChanged", "Active"); + +// ✅ NEW (Required) +using CommunityToolkit.Mvvm.Messaging; + +WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(userData)); +WeakReferenceMessenger.Default.Send(new StatusChangedMessage("Active")); +``` + +#### Step 4: Update Subscribe Calls + +```csharp +// ❌ OLD (Broken in .NET 10) +MessagingCenter.Subscribe(this, "UserLoggedIn", (sender, data) => +{ + // Handle message + CurrentUser = data; +}); + +// ✅ NEW (Required) +WeakReferenceMessenger.Default.Register(this, (recipient, message) => +{ + // Handle message + CurrentUser = message.Data; +}); +``` + +#### âš ī¸ Important Behavioral Difference: Duplicate Subscriptions + +**WeakReferenceMessenger** throws an `InvalidOperationException` if you try to register the same message type multiple times on the same recipient (MessagingCenter allowed this): + +```csharp +// ❌ This THROWS InvalidOperationException in WeakReferenceMessenger +WeakReferenceMessenger.Default.Register(this, (r, m) => Handler1(m)); +WeakReferenceMessenger.Default.Register(this, (r, m) => Handler2(m)); // ❌ THROWS! + +// ✅ Solution 1: Unregister before re-registering +WeakReferenceMessenger.Default.Unregister(this); +WeakReferenceMessenger.Default.Register(this, (r, m) => Handler1(m)); + +// ✅ Solution 2: Handle multiple actions in one registration +WeakReferenceMessenger.Default.Register(this, (r, m) => +{ + Handler1(m); + Handler2(m); +}); +``` + +**Why this matters:** If your code subscribes to the same message in multiple places (e.g., in a page constructor and in `OnAppearing`), you'll get a runtime crash. + +#### Step 5: Unregister When Done + +```csharp +// ❌ OLD +MessagingCenter.Unsubscribe(this, "UserLoggedIn"); + +// ✅ NEW (CRITICAL - prevents memory leaks) +WeakReferenceMessenger.Default.Unregister(this); + +// Or unregister all messages for this recipient +WeakReferenceMessenger.Default.UnregisterAll(this); +``` + +#### Complete Before/After Example + +**Before (.NET 9):** +```csharp +// Sender +public class LoginViewModel +{ + public async Task LoginAsync() + { + var user = await AuthService.LoginAsync(username, password); + MessagingCenter.Send(this, "UserLoggedIn", user); + } +} + +// Receiver +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + + MessagingCenter.Subscribe(this, "UserLoggedIn", (sender, user) => + { + WelcomeLabel.Text = $"Welcome, {user.Name}!"; + }); + } + + protected override void OnDisappearing() + { + base.OnDisappearing(); + MessagingCenter.Unsubscribe(this, "UserLoggedIn"); + } +} +``` + +**After (.NET 10):** +```csharp +// 1. Define message +public class UserLoggedInMessage +{ + public User User { get; } + + public UserLoggedInMessage(User user) + { + User = user; + } +} + +// 2. Sender +public class LoginViewModel +{ + public async Task LoginAsync() + { + var user = await AuthService.LoginAsync(username, password); + WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(user)); + } +} + +// 3. Receiver +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + + WeakReferenceMessenger.Default.Register(this, (recipient, message) => + { + WelcomeLabel.Text = $"Welcome, {message.User.Name}!"; + }); + } + + protected override void OnDisappearing() + { + base.OnDisappearing(); + WeakReferenceMessenger.Default.UnregisterAll(this); + } +} +``` + +**Key Differences:** +- ✅ Type-safe message classes +- ✅ No magic strings +- ✅ Better IntelliSense support +- ✅ Easier to refactor +- âš ī¸ **Must remember to unregister!** + +--- + +### ListView and TableView Deprecated + +**Status:** 🚨 **DEPRECATED (P0)** - `ListView`, `TableView`, and all Cell types are now obsolete. Migrate to `CollectionView`. + +**Warning You'll See:** +``` +warning CS0618: 'ListView' is obsolete: 'ListView is deprecated. Please use CollectionView instead.' +warning CS0618: 'TableView' is obsolete: 'Please use CollectionView instead.' +warning CS0618: 'TextCell' is obsolete: 'The controls which use TextCell (ListView and TableView) are obsolete. Please use CollectionView instead.' +``` + +**Obsolete Types:** +- `ListView` → `CollectionView` +- `TableView` → `CollectionView` (for settings pages, consider vertical StackLayout with BindableLayout) +- `TextCell` → Custom DataTemplate with Label(s) +- `ImageCell` → Custom DataTemplate with Image + Label(s) +- `EntryCell` → Custom DataTemplate with Entry +- `SwitchCell` → Custom DataTemplate with Switch +- `ViewCell` → DataTemplate + +**Impact:** This is a **MAJOR** breaking change. ListView and TableView are among the most commonly used controls in MAUI apps. + +#### Why This Takes Time + +Converting ListView/TableView to CollectionView is not a simple find-replace: + +1. **Different event model** - `ItemSelected` → `SelectionChanged` with different arguments +2. **Different grouping** - GroupDisplayBinding no longer exists +3. **Context actions** - Must convert to SwipeView +4. **Item sizing** - `HasUnevenRows` handled differently +5. **Platform-specific code** - iOS/Android ListView platform configurations need removal +6. **Testing required** - CollectionView virtualizes differently, may affect performance + +#### Migration Strategy + +**Step 1: Inventory Your ListViews** + +```bash +# Find all ListView/TableView usages +grep -r "ListView\|TableView" --include="*.xaml" --include="*.cs" . +``` + +**Step 2: Basic ListView → CollectionView** + +**Before (ListView):** +```xaml + + + + + + + +``` + +**After (CollectionView):** +```xaml + + + + + + + + +``` + +> âš ī¸ **Note:** CollectionView has `SelectionMode="None"` by default (selection disabled). You must explicitly set `SelectionMode="Single"` or `SelectionMode="Multiple"` to enable selection. + +**Code-behind changes:** +```csharp +// ❌ OLD (ListView) +void OnItemSelected(object sender, SelectedItemChangedEventArgs e) +{ + if (e.SelectedItem == null) + return; + + var item = (MyItem)e.SelectedItem; + // Handle selection + + // Deselect + ((ListView)sender).SelectedItem = null; +} + +// ✅ NEW (CollectionView) +void OnSelectionChanged(object sender, SelectionChangedEventArgs e) +{ + if (e.CurrentSelection.Count == 0) + return; + + var item = (MyItem)e.CurrentSelection.FirstOrDefault(); + // Handle selection + + // Deselect (optional) + ((CollectionView)sender).SelectedItem = null; +} +``` + +**Step 3: Grouped ListView → Grouped CollectionView** + +**Before (Grouped ListView):** +```xaml + + + + + + + +``` + +**After (Grouped CollectionView):** +```xaml + + + + + + + + + + + + + +``` + +**Step 4: Context Actions → SwipeView** + +> âš ī¸ **Platform Note:** SwipeView requires touch input. On Windows desktop, it only works with touch screens, not with mouse/trackpad. Consider providing alternative UI for desktop scenarios (e.g., buttons, right-click menu). + +**Before (ListView with ContextActions):** +```xaml + + + + + + + + + + +``` + +**After (CollectionView with SwipeView):** +```xaml + + + + + + + + + + + + + + +``` + +**Step 5: TableView for Settings → Alternative Approaches** + +TableView is commonly used for settings pages. Here are modern alternatives: + +**Option 1: CollectionView with Grouped Data** +```xaml + + + + + + + + + + + + + +``` + +**Option 2: Vertical StackLayout (for small settings lists)** +```xaml + + + + + + + + + + + + +``` + +**Step 6: Remove Platform-Specific ListView Code** + +If you used platform-specific ListView features, remove them: + +```csharp +// ❌ OLD - Remove these using statements (NOW OBSOLETE IN .NET 10) +using Microsoft.Maui.Controls.PlatformConfiguration; +using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; + +// ❌ OLD - Remove ListView platform configurations (NOW OBSOLETE IN .NET 10) +myListView.On().SetSeparatorStyle(SeparatorStyle.FullWidth); +myListView.On().IsFastScrollEnabled(); + +// ❌ OLD - Remove Cell platform configurations (NOW OBSOLETE IN .NET 10) +viewCell.On().SetDefaultBackgroundColor(Colors.White); +viewCell.On().SetIsContextActionsLegacyModeEnabled(false); +``` + +**Migration:** CollectionView does not have platform-specific configurations in the same way. If you need platform-specific styling: + +```csharp +// ✅ NEW - Use conditional compilation +#if IOS +var backgroundColor = Colors.White; +#elif ANDROID +var backgroundColor = Colors.Transparent; +#endif + +var grid = new Grid +{ + BackgroundColor = backgroundColor, + // ... rest of cell content +}; +``` + +Or in XAML: +```xaml + + + + + + + + + + + + + +``` + +#### Common Patterns & Pitfalls + +**1. Empty View** +```xaml + + + + + + + + + + +``` + +**2. Pull to Refresh** +```xaml + + + + + +``` + +**3. Item Spacing** +```xaml + + + + + + + +``` + +**4. Header and Footer** +```xaml + + + + + + + + + +``` + +**5. Load More / Infinite Scroll** +```xaml + + + +``` + +**6. Item Sizing Optimization** + +CollectionView uses `ItemSizingStrategy` to control item measurement: + +```xaml + + + + + + + + + +``` + +> 💡 **Performance Tip:** If your list items have consistent heights, use `ItemSizingStrategy="MeasureFirstItem"` for better performance with large lists. + +#### .NET 10 Handler Changes (iOS/Mac Catalyst) + +> â„šī¸ **.NET 10 uses new optimized CollectionView and CarouselView handlers** on iOS and Mac Catalyst by default, providing improved performance and stability. + +**If you previously opted-in to the new handlers in .NET 9**, you should now **REMOVE** this code: + +```csharp +// ❌ REMOVE THIS in .NET 10 (these handlers are now default) +#if IOS || MACCATALYST +builder.ConfigureMauiHandlers(handlers => +{ + handlers.AddHandler(); + handlers.AddHandler(); +}); +#endif +``` + +The optimized handlers are used automatically in .NET 10 - no configuration needed! + +**Only if you experience issues**, you can revert to the legacy handler: + +```csharp +// In MauiProgram.cs - only if needed +#if IOS || MACCATALYST +builder.ConfigureMauiHandlers(handlers => +{ + handlers.AddHandler(); +}); +#endif +``` + +However, Microsoft recommends using the new default handlers for best results. + +#### Testing Checklist + +After migration, test these scenarios: + +- [ ] **Item selection** works correctly +- [ ] **Grouped lists** display with proper headers +- [ ] **Swipe actions** (if used) work on both iOS and Android +- [ ] **Empty view** appears when list is empty +- [ ] **Pull to refresh** works (if used) +- [ ] **Scroll performance** is acceptable (especially for large lists) +- [ ] **Item sizing** is correct (CollectionView auto-sizes by default) +- [ ] **Selection visual state** shows/hides correctly +- [ ] **Data binding** updates the list correctly +- [ ] **Navigation** from list items works + +#### Migration Complexity Factors + +ListView to CollectionView migration is complex because: +- Each ListView may have unique behaviors +- Platform-specific code needs updating +- Extensive testing required +- Context actions need SwipeView conversion +- Grouped lists need template updates +- ViewModel changes may be needed + +#### Quick Reference: ListView vs CollectionView + +| Feature | ListView | CollectionView | +|---------|----------|----------------| +| **Selection Event** | `ItemSelected` | `SelectionChanged` | +| **Selection Args** | `SelectedItemChangedEventArgs` | `SelectionChangedEventArgs` | +| **Getting Selected** | `e.SelectedItem` | `e.CurrentSelection.FirstOrDefault()` | +| **Context Menus** | `ContextActions` | `SwipeView` | +| **Grouping** | `IsGroupingEnabled="True"` | `IsGrouped="true"` | +| **Group Header** | `GroupDisplayBinding` | `GroupHeaderTemplate` | +| **Even Rows** | `HasUnevenRows="False"` | Auto-sizes (default) | +| **Empty State** | Manual | `EmptyView` property | +| **Cells** | TextCell, ImageCell, etc. | Custom DataTemplate | + +--- + +## Deprecated APIs (P1 - Fix Soon) + +These APIs still work in .NET 10 but show compiler warnings. They will be removed in future versions. + +### 1. Animation Methods + +**Status:** âš ī¸ **DEPRECATED** - All sync animation methods replaced with async versions. + +**Warning You'll See:** +``` +warning CS0618: 'ViewExtensions.FadeTo(VisualElement, double, uint, Easing)' is obsolete: 'Please use FadeToAsync instead.' +``` + +**Migration Table:** + +| Old Method | New Method | Example | +|-----------|-----------|---------| +| `FadeTo()` | `FadeToAsync()` | `await view.FadeToAsync(0, 500);` | +| `ScaleTo()` | `ScaleToAsync()` | `await view.ScaleToAsync(1.5, 300);` | +| `TranslateTo()` | `TranslateToAsync()` | `await view.TranslateToAsync(100, 100, 250);` | +| `RotateTo()` | `RotateToAsync()` | `await view.RotateToAsync(360, 500);` | +| `RotateXTo()` | `RotateXToAsync()` | `await view.RotateXToAsync(45, 300);` | +| `RotateYTo()` | `RotateYToAsync()` | `await view.RotateYToAsync(45, 300);` | +| `ScaleXTo()` | `ScaleXToAsync()` | `await view.ScaleXToAsync(2.0, 300);` | +| `ScaleYTo()` | `ScaleYToAsync()` | `await view.ScaleYToAsync(2.0, 300);` | +| `RelRotateTo()` | `RelRotateToAsync()` | `await view.RelRotateToAsync(90, 300);` | +| `RelScaleTo()` | `RelScaleToAsync()` | `await view.RelScaleToAsync(0.5, 300);` | +| `LayoutTo()` | `LayoutToAsync()` | See special note below | + +#### Migration Examples + +**Simple Animation:** +```csharp +// ❌ OLD (Deprecated) +await myButton.FadeTo(0, 500); +await myButton.ScaleTo(1.5, 300); +await myButton.TranslateTo(100, 100, 250); + +// ✅ NEW (Required) +await myButton.FadeToAsync(0, 500); +await myButton.ScaleToAsync(1.5, 300); +await myButton.TranslateToAsync(100, 100, 250); +``` + +**Sequential Animations:** +```csharp +// ❌ OLD +await image.FadeTo(0, 300); +await image.ScaleTo(0.5, 300); +await image.FadeTo(1, 300); + +// ✅ NEW +await image.FadeToAsync(0, 300); +await image.ScaleToAsync(0.5, 300); +await image.FadeToAsync(1, 300); +``` + +**Parallel Animations:** +```csharp +// ❌ OLD +await Task.WhenAll( + image.FadeTo(0, 300), + image.ScaleTo(0.5, 300), + image.RotateTo(360, 300) +); + +// ✅ NEW +await Task.WhenAll( + image.FadeToAsync(0, 300), + image.ScaleToAsync(0.5, 300), + image.RotateToAsync(360, 300) +); +``` + +**With Cancellation:** +```csharp +// NEW: Async methods support cancellation +CancellationTokenSource cts = new(); + +try +{ + await view.FadeToAsync(0, 2000); +} +catch (TaskCanceledException) +{ + // Animation was cancelled +} + +// Cancel from elsewhere +cts.Cancel(); +``` + +#### Special Case: LayoutTo + +`LayoutToAsync()` is deprecated with a special message: "Use Translation to animate layout changes." + +```csharp +// ❌ OLD (Deprecated) +await view.LayoutToAsync(new Rect(100, 100, 200, 200), 250); + +// ✅ NEW (Use TranslateToAsync instead) +await view.TranslateToAsync(100, 100, 250); + +// Or animate Translation properties directly +var animation = new Animation(v => view.TranslationX = v, 0, 100); +animation.Commit(view, "MoveX", length: 250); +``` + +--- + +### 2. DisplayAlert and DisplayActionSheet + +**Status:** âš ī¸ **DEPRECATED** - Sync methods replaced with async versions. + +**Warning You'll See:** +``` +warning CS0618: 'Page.DisplayAlert(string, string, string)' is obsolete: 'Use DisplayAlertAsync instead' +``` + +#### Migration Examples + +**DisplayAlert:** +```csharp +// ❌ OLD (Deprecated) +await DisplayAlert("Success", "Data saved successfully", "OK"); +await DisplayAlert("Error", "Failed to save", "Cancel"); +bool result = await DisplayAlert("Confirm", "Delete this item?", "Yes", "No"); + +// ✅ NEW (Required) +await DisplayAlertAsync("Success", "Data saved successfully", "OK"); +await DisplayAlertAsync("Error", "Failed to save", "Cancel"); +bool result = await DisplayAlertAsync("Confirm", "Delete this item?", "Yes", "No"); +``` + +**DisplayActionSheet:** +```csharp +// ❌ OLD (Deprecated) +string action = await DisplayActionSheet( + "Choose an action", + "Cancel", + "Delete", + "Edit", "Share", "Duplicate" +); + +// ✅ NEW (Required) +string action = await DisplayActionSheetAsync( + "Choose an action", + "Cancel", + "Delete", + "Edit", "Share", "Duplicate" +); +``` + +**In ViewModels (with IDispatcher):** +```csharp +// If you're calling from a ViewModel, you'll need access to a Page +public class MyViewModel +{ + private readonly IDispatcher _dispatcher; + private readonly Page _page; + + public MyViewModel(IDispatcher dispatcher, Page page) + { + _dispatcher = dispatcher; + _page = page; + } + + public async Task ShowAlertAsync() + { + await _dispatcher.DispatchAsync(async () => + { + await _page.DisplayAlertAsync("Info", "Message from ViewModel", "OK"); + }); + } +} +``` + +--- + +### 3. Page.IsBusy + +**Status:** âš ī¸ **DEPRECATED** - Property will be removed in .NET 11. + +**Warning You'll See:** +``` +warning CS0618: 'Page.IsBusy' is obsolete: 'Page.IsBusy has been deprecated and will be removed in .NET 11' +``` + +**Why It's Deprecated:** +- Inconsistent behavior across platforms +- Limited customization options +- Doesn't work well with modern MVVM patterns + +#### Migration Examples + +**Simple Page:** +```xaml + + + + + + + + + + + + + + + + + +``` + +**With Loading Overlay:** +```xaml + + + + + + + + + + + + + + + + + +``` + +**In Code-Behind:** +```csharp +// ❌ OLD (Deprecated) +public partial class MyPage : ContentPage +{ + async Task LoadDataAsync() + { + IsBusy = true; + try + { + await LoadDataFromServerAsync(); + } + finally + { + IsBusy = false; + } + } +} + +// ✅ NEW (Recommended) +public partial class MyPage : ContentPage +{ + async Task LoadDataAsync() + { + LoadingIndicator.IsVisible = true; + LoadingIndicator.IsRunning = true; + try + { + await LoadDataFromServerAsync(); + } + finally + { + LoadingIndicator.IsVisible = false; + LoadingIndicator.IsRunning = false; + } + } +} +``` + +**In ViewModel:** +```csharp +public class MyViewModel : INotifyPropertyChanged +{ + private bool _isLoading; + public bool IsLoading + { + get => _isLoading; + set + { + _isLoading = value; + OnPropertyChanged(); + } + } + + public async Task LoadDataAsync() + { + IsLoading = true; + try + { + await LoadDataFromServerAsync(); + } + finally + { + IsLoading = false; + } + } +} +``` + +--- + +## Recommended Changes (P2) + +These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle. + +### Application.MainPage + +**Status:** âš ī¸ **DEPRECATED** - Property will be removed in future version. + +**Warning You'll See:** +``` +warning CS0618: 'Application.MainPage' is obsolete: 'This property is deprecated. Initialize your application by overriding Application.CreateWindow...' +``` + +#### Migration Example + +```csharp +// ❌ OLD (Deprecated) +public partial class App : Application +{ + public App() + { + InitializeComponent(); + MainPage = new AppShell(); + } + + // Changing page later + public void SwitchToLoginPage() + { + MainPage = new LoginPage(); + } +} + +// ✅ NEW (Recommended) +public partial class App : Application +{ + public App() + { + InitializeComponent(); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new AppShell()); + } + + // Changing page later + public void SwitchToLoginPage() + { + if (Windows.Count > 0) + { + Windows[0].Page = new LoginPage(); + } + } +} +``` + +**Benefits of CreateWindow:** +- Better multi-window support +- More explicit initialization +- Cleaner separation of concerns +- Works better with Shell + +--- + +## Bulk Migration Tools + +Use these find/replace patterns to quickly update your codebase. + +### Visual Studio / VS Code + +**Regex Mode - Find/Replace** + +#### Animation Methods + +```regex +Find: \.FadeTo\( +Replace: .FadeToAsync( + +Find: \.ScaleTo\( +Replace: .ScaleToAsync( + +Find: \.TranslateTo\( +Replace: .TranslateToAsync( + +Find: \.RotateTo\( +Replace: .RotateToAsync( + +Find: \.RotateXTo\( +Replace: .RotateXToAsync( + +Find: \.RotateYTo\( +Replace: .RotateYToAsync( + +Find: \.ScaleXTo\( +Replace: .ScaleXToAsync( + +Find: \.ScaleYTo\( +Replace: .ScaleYToAsync( + +Find: \.RelRotateTo\( +Replace: .RelRotateToAsync( + +Find: \.RelScaleTo\( +Replace: .RelScaleToAsync( +``` + +#### Display Methods + +```regex +Find: DisplayAlert\( +Replace: DisplayAlertAsync( + +Find: DisplayActionSheet\( +Replace: DisplayActionSheetAsync( +``` + +#### ListView/TableView Detection (Manual Migration Required) + +**âš ī¸ Note:** ListView/TableView migration CANNOT be automated. Use these searches to find instances: + +```bash +# Find all ListView usages in XAML +grep -r " migration-report.txt +echo "" >> migration-report.txt +echo "XAML ListView instances:" >> migration-report.txt +grep -rn "> migration-report.txt +echo "" >> migration-report.txt +echo "XAML TableView instances:" >> migration-report.txt +grep -rn "> migration-report.txt +echo "" >> migration-report.txt +echo "ItemSelected handlers:" >> migration-report.txt +grep -rn "ItemSelected" --include="*.xaml" --include="*.cs" . >> migration-report.txt +echo "" >> migration-report.txt +cat migration-report.txt +``` + +### PowerShell Script + +```powershell +# Replace animation methods in all .cs files +Get-ChildItem -Path . -Recurse -Filter *.cs | ForEach-Object { + $content = Get-Content $_.FullName -Raw + + # Animation methods + $content = $content -replace '\.FadeTo\(', '.FadeToAsync(' + $content = $content -replace '\.ScaleTo\(', '.ScaleToAsync(' + $content = $content -replace '\.TranslateTo\(', '.TranslateToAsync(' + $content = $content -replace '\.RotateTo\(', '.RotateToAsync(' + $content = $content -replace '\.RotateXTo\(', '.RotateXToAsync(' + $content = $content -replace '\.RotateYTo\(', '.RotateYToAsync(' + $content = $content -replace '\.ScaleXTo\(', '.ScaleXToAsync(' + $content = $content -replace '\.ScaleYTo\(', '.ScaleYToAsync(' + $content = $content -replace '\.RelRotateTo\(', '.RelRotateToAsync(' + $content = $content -replace '\.RelScaleTo\(', '.RelScaleToAsync(' + + # Display methods + $content = $content -replace 'DisplayAlert\(', 'DisplayAlertAsync(' + $content = $content -replace 'DisplayActionSheet\(', 'DisplayActionSheetAsync(' + + Set-Content $_.FullName $content +} + +Write-Host "✅ Migration complete!" +``` + +--- + +## Testing Your Upgrade + +### Build Validation + +```bash +# Clean solution +dotnet clean + +# Restore packages +dotnet restore + +# Build for each platform +dotnet build -f net10.0-android -c Release +dotnet build -f net10.0-ios -c Release +dotnet build -f net10.0-maccatalyst -c Release +dotnet build -f net10.0-windows -c Release + +# Check for warnings +dotnet build --no-incremental 2>&1 | grep -i "warning CS0618" +``` + +### Enable Warnings as Errors (Temporary) + +```xml + + + CS0618 + +``` + +### Test Checklist + +- [ ] App launches successfully on all platforms +- [ ] All animations work correctly +- [ ] Dialogs (alerts/action sheets) display properly +- [ ] Loading indicators work (if you used IsBusy) +- [ ] Inter-component communication works (MessagingCenter replacement) +- [ ] No CS0618 warnings in build output +- [ ] No runtime exceptions related to obsolete APIs + +--- + +## Troubleshooting + +### Error: 'MessagingCenter' is inaccessible due to its protection level + +**Cause:** MessagingCenter is now internal in .NET 10. + +**Solution:** +1. Install `CommunityToolkit.Mvvm` package +2. Replace with `WeakReferenceMessenger` (see [MessagingCenter section](#messagingcenter-made-internal)) +3. Create message classes for each message type +4. Don't forget to unregister! + +--- + +### Warning: Animation method is obsolete + +**Cause:** Using sync animation methods (`FadeTo`, `ScaleTo`, etc.) + +**Quick Fix:** +```bash +# Use PowerShell script from Bulk Migration Tools section +# Or use Find/Replace patterns +``` + +**Manual Fix:** +Add `Async` to the end of each animation method call: +- `FadeTo` → `FadeToAsync` +- `ScaleTo` → `ScaleToAsync` +- etc. + +--- + +### Page.IsBusy doesn't work anymore + +**Cause:** IsBusy still works but is deprecated. + +**Solution:** Replace with explicit ActivityIndicator (see [IsBusy section](#3-pageisbusy)) + +--- + +### Build fails with "Target framework 'net10.0' not found" + +**Cause:** .NET 10 SDK not installed or not latest version. + +**Solution:** +```bash +# Check SDK version +dotnet --version # Should be 10.0.100 or later + +# Install .NET 10 SDK from: +# https://dotnet.microsoft.com/download/dotnet/10.0 + +# Update workloads +dotnet workload update +``` + +--- + +### MessagingCenter migration breaks existing code + +**Common Issues:** + +1. **Forgot to unregister:** + ```csharp + // âš ī¸ Memory leak if you don't unregister + protected override void OnDisappearing() + { + base.OnDisappearing(); + WeakReferenceMessenger.Default.UnregisterAll(this); + } + ``` + +2. **Wrong message type:** + ```csharp + // ❌ Wrong + WeakReferenceMessenger.Default.Register(this, handler); + WeakReferenceMessenger.Default.Send(new UserData()); // Wrong type! + + // ✅ Correct + WeakReferenceMessenger.Default.Register(this, handler); + WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(userData)); + ``` + +3. **Recipient parameter confusion:** + ```csharp + // The recipient parameter is the object that registered (this) + WeakReferenceMessenger.Default.Register(this, (recipient, message) => + { + // recipient == this + // message == the message that was sent + }); + ``` + +--- + +### Animation doesn't complete after migration + +**Cause:** Forgetting `await` keyword. + +```csharp +// ❌ Wrong - animation runs but code continues immediately +view.FadeToAsync(0, 500); +DoSomethingElse(); + +// ✅ Correct - wait for animation to complete +await view.FadeToAsync(0, 500); +DoSomethingElse(); +``` + +--- + +### Warning: ListView/TableView/TextCell is obsolete + +**Cause:** Using deprecated ListView, TableView, or Cell types. + +**Solution:** Migrate to CollectionView (see [ListView and TableView section](#listview-and-tableview-deprecated)) + +**Quick Decision Guide:** +- **Simple list** → CollectionView with custom DataTemplate +- **Settings page with <20 items** → VerticalStackLayout with BindableLayout +- **Settings page with 20+ items** → Grouped CollectionView +- **Grouped data list** → CollectionView with `IsGrouped="True"` + +--- + +### CollectionView doesn't have SelectedItem event + +**Cause:** CollectionView uses `SelectionChanged` instead of `ItemSelected`. + +**Solution:** +```csharp +// ❌ OLD (ListView) +void OnItemSelected(object sender, SelectedItemChangedEventArgs e) +{ + var item = e.SelectedItem as MyItem; +} + +// ✅ NEW (CollectionView) +void OnSelectionChanged(object sender, SelectionChangedEventArgs e) +{ + var item = e.CurrentSelection.FirstOrDefault() as MyItem; +} +``` + +--- + +### Platform-specific ListView configuration is obsolete + +**Cause:** Using `Microsoft.Maui.Controls.PlatformConfiguration.*Specific.ListView` extensions. + +**Error:** +``` +warning CS0618: 'ListView' is obsolete: 'With the deprecation of ListView, this class is obsolete. Please use CollectionView instead.' +``` + +**Solution:** +1. Remove platform-specific ListView using statements: + ```csharp + // ❌ Remove these + using Microsoft.Maui.Controls.PlatformConfiguration; + using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; + using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; + ``` + +2. Remove platform-specific ListView calls: + ```csharp + // ❌ Remove these + myListView.On().SetSeparatorStyle(SeparatorStyle.FullWidth); + myListView.On().IsFastScrollEnabled(); + viewCell.On().SetDefaultBackgroundColor(Colors.White); + ``` + +3. CollectionView has different platform customization options - consult CollectionView docs for alternatives. + +--- + +### CollectionView performance issues after ListView migration + +**Common Causes:** + +1. **Not using DataTemplate caching:** + ```xaml + + + + + + + + + + + + + + + ``` + +2. **Complex nested layouts:** + - Avoid deeply nested layouts in ItemTemplate + - Use Grid instead of StackLayout when possible + - Consider FlexLayout for complex layouts + +3. **Images not being cached:** + ```xaml + + + + + + ``` + +--- + +## Quick Reference Card + +### Priority Checklist + +**Must Fix (P0 - Breaking/Critical):** +- [ ] Replace `MessagingCenter` with `WeakReferenceMessenger` +- [ ] Migrate `ListView` to `CollectionView` +- [ ] Migrate `TableView` to `CollectionView` or `BindableLayout` +- [ ] Replace `TextCell`, `ImageCell`, etc. with custom DataTemplates +- [ ] Convert `ContextActions` to `SwipeView` +- [ ] Remove platform-specific ListView configurations + +**Should Fix (P1 - Deprecated):** +- [ ] Update animation methods: add `Async` suffix +- [ ] Update `DisplayAlert` → `DisplayAlertAsync` +- [ ] Update `DisplayActionSheet` → `DisplayActionSheetAsync` +- [ ] Replace `Page.IsBusy` with `ActivityIndicator` + +**Nice to Have (P2):** +- [ ] Migrate `Application.MainPage` to `CreateWindow` + +### Common Patterns + +```csharp +// Animation +await view.FadeToAsync(0, 500); + +// Alert +await DisplayAlertAsync("Title", "Message", "OK"); + +// Messaging +WeakReferenceMessenger.Default.Send(new MyMessage()); +WeakReferenceMessenger.Default.Register(this, (r, m) => { }); +WeakReferenceMessenger.Default.UnregisterAll(this); + +// Loading +IsLoading = true; +try { await LoadAsync(); } +finally { IsLoading = false; } +``` + +--- + +## Additional Resources + +- **Official Docs:** https://learn.microsoft.com/dotnet/maui/ +- **Migration Guide:** https://learn.microsoft.com/dotnet/maui/migration/ +- **GitHub Issues:** https://github.com/dotnet/maui/issues +- **CommunityToolkit.Mvvm:** https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/ + +--- + +**Document Version:** 2.0 +**Last Updated:** November 2025 +**Applies To:** .NET MAUI 10.0.100 and later