Skip to content

feature: Deprecate obsolete MAUI Cell controls and introduce CollectionView-compatible alternatives #4144

@glennawatson

Description

@glennawatson

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 DataTemplates 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>.

  1. **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
  2. **Create ReactiveImageItemView.cs

    • This view will serve as a replacement for the common ImageCell layout.
    • It should contain an Image control and two Label controls.
    • Match the rest of the project and don't provide XAML files
    • Make sure back compatible with .net8/9
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.

  1. 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);
            });
        }
    }
  2. Run the sample application and scroll the list up and down quickly.

  3. Observe the debug output. You must see a DEACTIVATED message for every item that scrolls out of view. For every ACTIVATED message, there must eventually be a corresponding DEACTIVATED message.

  4. 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 new CollectionView/DataTemplate pattern.
  • New ReactiveTextItemView and ReactiveImageItemView components are created as ReactiveContentView-based controls.
  • All new code adheres to the project's established coding standards and conventions.

Resources

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions