-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
MAUI: Deprecate Reactive*Cell Controls and Introduce CollectionView Alternatives
Context and Motivation
The .NET MAUI team is officially deprecating ListView
and its associated Cell
types (TextCell
, ViewCell
, etc.) starting in .NET 10. The recommended replacement is CollectionView
, which offers superior performance, layout flexibility, and is the focus of future development for the .NET MAUI framework. You can read the official announcement on the dotnet/maui repository: Issue #28699.
We want to maintain back compatibility where able with .NET 8 and 9.
To align ReactiveUI.Maui
with modern .NET MAUI best practices and ensure our users have a clear path forward, we need to deprecate our corresponding Reactive*Cell
controls and provide new, CollectionView
-compatible alternatives. This task is crucial for the future health of the library and provides a great opportunity to improve the developer experience for list-based UIs.
The goal is to guide users towards using DataTemplate
s with CollectionView
, which is the standard pattern in .NET MAUI. We will achieve this by deprecating the old controls and introducing new ReactiveContentView
-based components that can be easily used within a DataTemplate
.
Contributor Guide: Step-by-Step Instructions
Step 1: Investigation and Inventory
Your first task is to locate all Reactive*Cell.cs
files within the ReactiveUI.Maui
source directory. These will likely include:
ReactiveTextCell.cs
ReactiveImageCell.cs
ReactiveViewCell.cs
ReactiveSwitchCell.cs
ReactiveEntryCell.cs
Step 2: Mark Existing Cells as Obsolete
For each of the files identified in Step 1, add the [Obsolete]
attribute to the class definition. The message provided in the attribute is critical for guiding users to the new pattern. Please use a helpful and consistent message format.
Example for ReactiveTextCell
:
[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")]
public partial class ReactiveTextCell<TViewModel> : TextCell, IViewFor<TViewModel>
where TViewModel : class
{
//... existing code
}
Apply a similar, appropriately worded message to all other Reactive*Cell
classes.
Step 3: Create New ReactiveContentView
-based Components
The modern approach is to use reusable views inside a DataTemplate
. We will create two new reference components to demonstrate this pattern. These components should inherit from ReactiveContentView<TViewModel>
.
-
**Create
ReactiveTextItemView.cs
- This view will serve as a replacement for the common
TextCell
layout. - It should contain two
Label
controls for primary and detail text. - The view model should be a generic
TViewModel
that the view binds to. - Match the rest of the project and don't provide XAML files
- Make sure back compatible with .net8/9
- This view will serve as a replacement for the common
-
**Create
ReactiveImageItemView.cs
- This view will serve as a replacement for the common
ImageCell
layout. - It should contain an
Image
control and twoLabel
controls. - Match the rest of the project and don't provide XAML files
- Make sure back compatible with .net8/9
- This view will serve as a replacement for the common
Step 4: CRITICAL - Verify ViewModel Activation Lifecycle
This is the most important step. CollectionView
aggressively recycles its views to maintain performance. This can cause issues with activation and deactivation if not handled correctly, leading to memory leaks.
You must verify that the WhenActivated
block in your item's view model is correctly set up and torn down during scrolling. Create unit tests to verify this works.
-
In the view model for your list items, add logging inside the
WhenActivated
disposable.public class MyItemViewModel : ReactiveObject, IActivatableViewModel { public ViewModelActivator Activator { get; } = new(); public string Title { get; } public MyItemViewModel(string title) { Title = title; this.WhenActivated(disposables => { System.Diagnostics.Debug.WriteLine($"ACTIVATED: {Title}"); Disposable .Create(() => System.Diagnostics.Debug.WriteLine($"DEACTIVATED: {Title}")) .DisposeWith(disposables); }); } }
-
Run the sample application and scroll the list up and down quickly.
-
Observe the debug output. You must see a
DEACTIVATED
message for every item that scrolls out of view. For everyACTIVATED
message, there must eventually be a correspondingDEACTIVATED
message. -
Confirm that there are no memory leaks by ensuring disposables are cleaned up correctly.
Acceptance Criteria
A pull request for this issue will be considered complete when it meets the following criteria:
- All legacy
Reactive*Cell
classes are marked with the[Obsolete]
attribute, including a helpful message pointing to the newCollectionView
/DataTemplate
pattern. - New
ReactiveTextItemView
andReactiveImageItemView
components are created asReactiveContentView
-based controls. - All new code adheres to the project's established coding standards and conventions.
Resources
- .NET MAUI CollectionView Documentation: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/
- .NET MAUI DataTemplate Documentation: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/datatemplate
- Official ListView Deprecation GitHub Issue: Deprecation of
ListView
in .NET 10 MAUI dotnet/maui#28699