---
description: 'Instructions for upgrading .NET MAUI applications from version 9 to version 10, including breaking changes, deprecated APIs, and migration strategies for ListView to CollectionView.'
applyTo: '**/*.csproj, **/*.cs, **/*.xaml'
---
# 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.19041.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.19041.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