К основному содержанию
правило официальный · GitHub

dotnet-maui-9-to-dotnet-maui-10-upgrade — правило GitHub Copilot

Помогает обновить проект .NET MAUI с 9 на 10 версию: правит смену TargetFramework, замену MessagingCenter на WeakReferenceMessenger и переезд с ListView на CollectionView.

Установить в GitHub Copilot · Вручную: положить файлы Все способы и платформы ↓
.github/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md
Открыть на GitHub

Что делает dotnet-maui-9-to-dotnet-maui-10-upgrade

Правило применяется к файлам *.csproj, *.cs и *.xaml. Оно ведёт миграцию по шагам. Сначала меняется TargetFramework на net10.0. Для сборки в Linux или GitHub Codespaces добавляются условия, отсекающие платформы Apple.

Основной упор сделан на устранение ошибок компиляции. В .NET 10 класс MessagingCenter стал закрытым (internal). Правило переводит вызовы на WeakReferenceMessenger из пакета CommunityToolkit.Mvvm 8.3.0 с созданием отдельных классов сообщений. При этом учитывается смена поведения: повторная регистрация одного типа сообщения на получателе выбрасывает InvalidOperationException.

Инструкция также находит устаревшие ListView и TableView для замены на CollectionView. Под замену попадают старые анимации, DisplayAlert и свойство Page.IsBusy. Без обновления CommunityToolkit.Maui минимум до версии 12.3.0 проект на .NET 10 не скомпилируется.

Какие задачи решает

  • перевести csproj на целевой фреймворк net10.0
  • заменить закрытый MessagingCenter на WeakReferenceMessenger
  • настроить сборку проекта под Linux и Codespaces без сбоев из-за таргетов iOS
  • спланировать миграцию устаревших ListView и TableView на CollectionView

Примеры запросов агенту

  • Обнови проект MAUI на .NET 10 и проверь несовместимые вызовы MessagingCenter
  • Сконфигурируй csproj для net10.0 с поддержкой сборки на Linux в Codespaces
  • Перепиши устаревшие ListView на CollectionView по правилам миграции на .NET 10

Как установить dotnet-maui-9-to-dotnet-maui-10-upgrade

Вручную: положить файлы

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

Поле applyTo задаёт, к каким файлам применять

По шагам, с командами для каждой платформы: AGENTS.md и CLAUDE.md: что это, кто читает и какой выбрать

Вопросы о dotnet-maui-9-to-dotnet-maui-10-upgrade

Как установить dotnet-maui-9-to-dotnet-maui-10-upgrade в GitHub Copilot?

Сохраните файл по пути .github/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md в корне репозитория. GitHub Copilot автоматически применит инструкции к файлам .csproj, .cs и .xaml.

Зачем нужно это правило?

Оно структурирует обновление .NET MAUI с 9 на 10 версию, помогая устранить критические ошибки сборки из-за внутренних API и устаревших компонентов UI.

Что меняется при замене MessagingCenter на WeakReferenceMessenger?

Повторная регистрация одного и того же типа сообщения на том же объекте выбрасывает InvalidOperationException. Чтобы избежать падения в рантайме, нужно отписываться перед повторной подпиской или объединять действия в один обработчик.

Исходный текст

dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md · MIT
---
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)
   - [Animation Methods](#1-animation-methods)
   - [DisplayAlert and DisplayActionSheet](#2-displayalert-and-displayactionsheet)
   - [Page.IsBusy](#3-pageisbusy)
   - [MediaPicker APIs](#4-mediapicker-apis)
5. [Recommended Changes (P2)](#recommended-changes-p2)
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, MediaPicker (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
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
</Project>
```

### Multi-Platform

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.19041.0</TargetFrameworks>
  </PropertyGroup>
</Project>
```

### 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
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- Start with Android (always supported) -->
    <TargetFrameworks>net10.0-android</TargetFrameworks>
    
    <!-- Add iOS/Mac Catalyst only when NOT on Linux -->
    <TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
    
    <!-- Add Windows only when on Windows -->
    <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
  </PropertyGroup>
</Project>
```

**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<App, string>(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<App, UserData>(this, "UserLoggedIn", (sender, data) =>
{
    // Handle message
    CurrentUser = data;
});

// ✅ NEW (Required)
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(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<UserLoggedInMessage>(this, (r, m) => Handler1(m));
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) => Handler2(m)); // ❌ THROWS!

// ✅ Solution 1: Unregister before re-registering
WeakReferenceMessenger.Default.Unregister<UserLoggedInMessage>(this);
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) => Handler1(m));

// ✅ Solution 2: Handle multiple actions in one registration
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(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<App, UserData>(this, "UserLoggedIn");

// ✅ NEW (CRITICAL - prevents memory leaks)
WeakReferenceMessenger.Default.Unregister<UserLoggedInMessage>(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<LoginViewModel, User>(this, "UserLoggedIn", (sender, user) =>
        {
            WelcomeLabel.Text = $"Welcome, {user.Name}!";
        });
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        MessagingCenter.Unsubscribe<LoginViewModel, User>(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<UserLoggedInMessage>(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
<ListView ItemsSource="{Binding Items}"
          ItemSelected="OnItemSelected"
          HasUnevenRows="True">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Title}"
                     Detail="{Binding Description}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
```

**After (CollectionView):**
```xaml
<CollectionView ItemsSource="{Binding Items}"
                SelectionMode="Single"
                SelectionChanged="OnSelectionChanged">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <VerticalStackLayout Padding="10">
                <Label Text="{Binding Title}" 
                       FontAttributes="Bold" />
                <Label Text="{Binding Description}"
                       FontSize="12"
                       TextColor="{StaticResource Gray600}" />
            </VerticalStackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
```

> ⚠️ **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
<ListView ItemsSource="{Binding GroupedItems}"
          IsGroupingEnabled="True"
          GroupDisplayBinding="{Binding Key}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Name}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
```

**After (Grouped CollectionView):**
```xaml
<CollectionView ItemsSource="{Binding GroupedItems}"
                IsGrouped="true">
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate>
            <Label Text="{Binding Key}"
                   FontAttributes="Bold"
                   BackgroundColor="{StaticResource Gray100}"
                   Padding="10,5" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
    
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <VerticalStackLayout Padding="20,10">
                <Label Text="{Binding Name}" />
            </VerticalStackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
```

**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
<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell>
            <ViewCell.ContextActions>
                <MenuItem Text="Delete" 
                         IsDestructive="True"
                         Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyPage}}, Path=DeleteCommand}"
                         CommandParameter="{Binding .}" />
            </ViewCell.ContextActions>
            
            <Label Text="{Binding Title}" Padding="10" />
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>
```

**After (CollectionView with SwipeView):**
```xaml
<CollectionView.ItemTemplate>
    <DataTemplate>
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItems>
                    <SwipeItem Text="Delete"
                              BackgroundColor="Red"
                              Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyPage}}, Path=DeleteCommand}"
                              CommandParameter="{Binding .}" />
                </SwipeItems>
            </SwipeView.RightItems>
            
            <VerticalStackLayout Padding="10">
                <Label Text="{Binding Title}" />
            </VerticalStackLayout>
        </SwipeView>
    </DataTemplate>
</CollectionView.ItemTemplate>
```

**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
<CollectionView ItemsSource="{Binding SettingGroups}"
                IsGrouped="true"
                SelectionMode="None">
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate>
            <Label Text="{Binding Title}" 
                   FontAttributes="Bold"
                   Margin="10,15,10,5" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
    
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="15,10" ColumnDefinitions="*,Auto">
                <Label Text="{Binding Title}" 
                       VerticalOptions="Center" />
                <Switch Grid.Column="1" 
                        IsToggled="{Binding IsEnabled}"
                        IsVisible="{Binding ShowSwitch}" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
```

**Option 2: Vertical StackLayout (for small settings lists)**
```xaml
<ScrollView>
    <VerticalStackLayout BindableLayout.ItemsSource="{Binding Settings}"
                        Spacing="10"
                        Padding="15">
        <BindableLayout.ItemTemplate>
            <DataTemplate>
                <Border StrokeThickness="0"
                       BackgroundColor="{StaticResource Gray100}"
                       Padding="15,10">
                    <Grid ColumnDefinitions="*,Auto">
                        <Label Text="{Binding Title}" 
                              VerticalOptions="Center" />
                        <Switch Grid.Column="1" 
                               IsToggled="{Binding IsEnabled}" />
                    </Grid>
                </Border>
            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </VerticalStackLayout>
</ScrollView>
```

**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<iOS>().SetSeparatorStyle(SeparatorStyle.FullWidth);
myListView.On<Android>().IsFastScrollEnabled();

// ❌ OLD - Remove Cell platform configurations (NOW OBSOLETE IN .NET 10)
viewCell.On<iOS>().SetDefaultBackgroundColor(Colors.White);
viewCell.On<Android>().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
<CollectionView.ItemTemplate>
    <DataTemplate>
        <Grid>
            <Grid.BackgroundColor>
                <OnPlatform x:TypeArguments="Color">
                    <On Platform="iOS" Value="White" />
                    <On Platform="Android" Value="Transparent" />
                </OnPlatform>
            </Grid.BackgroundColor>
            <!-- Cell content -->
        </Grid>
    </DataTemplate>
</CollectionView.ItemTemplate>
```

#### Common Patterns & Pitfalls

**1. Empty View**
```xaml
<!-- CollectionView has built-in EmptyView support -->
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.EmptyView>
        <ContentView>
            <VerticalStackLayout Padding="50" VerticalOptions="Center">
                <Label Text="No items found" 
                       HorizontalTextAlignment="Center" />
            </VerticalStackLayout>
        </ContentView>
    </CollectionView.EmptyView>
    <!-- ... -->
</CollectionView>
```

**2. Pull to Refresh**
```xaml
<RefreshView IsRefreshing="{Binding IsRefreshing}"
             Command="{Binding RefreshCommand}">
    <CollectionView ItemsSource="{Binding Items}">
        <!-- ... -->
    </CollectionView>
</RefreshView>
```

**3. Item Spacing**
```xaml
<!-- Use ItemsLayout for spacing -->
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical" 
                          ItemSpacing="10" />
    </CollectionView.ItemsLayout>
    <!-- ... -->
</CollectionView>
```

**4. Header and Footer**
```xaml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.Header>
        <Label Text="My List" 
               FontSize="24" 
               Padding="10" />
    </CollectionView.Header>
    
    <CollectionView.Footer>
        <Label Text="End of list" 
               Padding="10" 
               HorizontalTextAlignment="Center" />
    </CollectionView.Footer>
    
    <!-- ItemTemplate -->
</CollectionView>
```

**5. Load More / Infinite Scroll**
```xaml
<CollectionView ItemsSource="{Binding Items}"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}">
    <!-- ItemTemplate -->
</CollectionView>
```

**6. Item Sizing Optimization**

CollectionView uses `ItemSizingStrategy` to control item measurement:

```xaml
<!-- Default: Each item measured individually (like HasUnevenRows="True") -->
<CollectionView ItemSizingStrategy="MeasureAllItems">
    <!-- ... -->
</CollectionView>

<!-- Performance: Only first item measured, rest use same height -->
<CollectionView ItemSizingStrategy="MeasureFirstItem">
    <!-- Use this when all items have similar heights -->
</CollectionView>
```

> 💡 **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<CollectionView, CollectionViewHandler2>();
    handlers.AddHandler<CarouselView, CarouselViewHandler2>();
});
#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<Microsoft.Maui.Controls.CollectionView, 
                        Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler>();
});
#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
<!-- ❌ OLD (Deprecated) -->
<ContentPage IsBusy="{Binding IsLoading}">
    <StackLayout>
        <Label Text="Content here" />
    </StackLayout>
</ContentPage>

<!-- ✅ NEW (Recommended) -->
<ContentPage>
    <Grid>
        <!-- Main content -->
        <StackLayout>
            <Label Text="Content here" />
        </StackLayout>
        
        <!-- Loading indicator overlay -->
        <ActivityIndicator IsRunning="{Binding IsLoading}"
                          IsVisible="{Binding IsLoading}"
                          Color="{StaticResource Primary}"
                          VerticalOpti...

© github · MIT · github/awesome-copilot

Похожие правила IDE

Всё по теме «Мобильная разработка» →

Из того же набора770

Весь набор →
draw-io Задаёт Copilot правила генерации и правки диаграмм draw.io: структура mxGraph XML, палитра цветов, сетка и проверка файла перед коммитом. ★ 40 418звёзд репо dotnet-wpf Настраивает GitHub Copilot на генерацию кода для WPF на .NET 8.0 и новее с соблюдением паттерна MVVM. Помогает держать логику во ViewModel и избегать кода в code-behind. ★ 40 418звёзд репо dotnet-upgrade Правило ведёт поэтапное обновление .NET-решения: от анализа проектов и зависимостей до сборки, тестов и проверки CI/CD. ★ 40 418звёзд репо dotnet-maui Правило задаёт Copilot стиль кода .NET MAUI: MVVM, CollectionView вместо ListView, Shell-навигация. Пригодится, если агент лезет в XAML и C#. ★ 40 418звёзд репо dotnet-framework Правило направляет Copilot при работе с проектами .NET Framework: учитывает формат .csproj, ограничения C# 7.3 и практики для асинхронного кода. Подключите его, чтобы агент не предлагал неподдерживаемый синтаксис. ★ 40 418звёзд репо devops-core-principles Инструкции для GitHub Copilot по культуре CALMS, метрикам DORA и сквозной автоматизации от CI/CD до IaC. ★ 40 418звёзд репо devcontainers Двенадцать правил, по которым Copilot проверяет devcontainer.json, Dev Container Features и lifecycle-настройки Codespaces, с конкретной правкой на каждый пункт. ★ 40 418звёзд репо devbox-image-definition Правило для GitHub Copilot, которое помогает писать YAML-файлы описания образов для Microsoft Dev Box Team Customizations. Пригодится, если вы собираете кастомизации через задачи ~/winget, ~/gitclone и ~/powershell. ★ 40 418звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой