From 1ef715ea76149df5106a70f2c54501bd6d94fdc6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Sep 2025 06:04:17 +0000 Subject: [PATCH 1/4] Initial plan From 38508d99a4c8d2b329e5c42e4dd6b888feeb9768 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Sep 2025 06:09:39 +0000 Subject: [PATCH 2/4] Add Obsolete attributes to all Reactive*Cell classes and create new CollectionView-compatible components Co-authored-by: glennawatson <5834289+glennawatson@users.noreply.github.com> --- src/ReactiveUI.Maui/ReactiveEntryCell.cs | 1 + src/ReactiveUI.Maui/ReactiveImageCell.cs | 1 + src/ReactiveUI.Maui/ReactiveImageItemView.cs | 171 +++++++++++++++++++ src/ReactiveUI.Maui/ReactiveSwitchCell.cs | 1 + src/ReactiveUI.Maui/ReactiveTextCell.cs | 1 + src/ReactiveUI.Maui/ReactiveTextItemView.cs | 133 +++++++++++++++ src/ReactiveUI.Maui/ReactiveViewCell.cs | 1 + 7 files changed, 309 insertions(+) create mode 100644 src/ReactiveUI.Maui/ReactiveImageItemView.cs create mode 100644 src/ReactiveUI.Maui/ReactiveTextItemView.cs diff --git a/src/ReactiveUI.Maui/ReactiveEntryCell.cs b/src/ReactiveUI.Maui/ReactiveEntryCell.cs index f32bde2a4b..f7aabcb45a 100644 --- a/src/ReactiveUI.Maui/ReactiveEntryCell.cs +++ b/src/ReactiveUI.Maui/ReactiveEntryCell.cs @@ -17,6 +17,7 @@ namespace ReactiveUI.Maui; [RequiresDynamicCode("ReactiveEntryCell uses methods that require dynamic code generation")] [RequiresUnreferencedCode("ReactiveEntryCell uses methods that may require unreferenced code")] #endif +[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 ReactiveEntryCell : EntryCell, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI.Maui/ReactiveImageCell.cs b/src/ReactiveUI.Maui/ReactiveImageCell.cs index df092bfcc3..cefdd20f4f 100644 --- a/src/ReactiveUI.Maui/ReactiveImageCell.cs +++ b/src/ReactiveUI.Maui/ReactiveImageCell.cs @@ -17,6 +17,7 @@ namespace ReactiveUI.Maui; [RequiresDynamicCode("ReactiveImageCell uses methods that require dynamic code generation")] [RequiresUnreferencedCode("ReactiveImageCell uses methods that may require unreferenced code")] #endif +[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 ReactiveImageCell : ImageCell, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI.Maui/ReactiveImageItemView.cs b/src/ReactiveUI.Maui/ReactiveImageItemView.cs new file mode 100644 index 0000000000..ebd1f65506 --- /dev/null +++ b/src/ReactiveUI.Maui/ReactiveImageItemView.cs @@ -0,0 +1,171 @@ +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using Microsoft.Maui.Controls; + +namespace ReactiveUI.Maui; + +/// +/// A that displays an image with text content similar to an ImageCell, +/// but designed for use with CollectionView and DataTemplates. This serves as a modern replacement +/// for ReactiveImageCell which relied on the deprecated ListView. +/// +/// The type of the view model. +/// +#if NET6_0_OR_GREATER +[RequiresDynamicCode("ReactiveImageItemView uses methods that require dynamic code generation")] +[RequiresUnreferencedCode("ReactiveImageItemView uses methods that may require unreferenced code")] +#endif +public partial class ReactiveImageItemView : ReactiveContentView + where TViewModel : class +{ + /// + /// The image source bindable property. + /// + public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create( + nameof(ImageSource), + typeof(ImageSource), + typeof(ReactiveImageItemView), + default(ImageSource)); + + /// + /// The text bindable property for the primary text. + /// + public static readonly BindableProperty TextProperty = BindableProperty.Create( + nameof(Text), + typeof(string), + typeof(ReactiveImageItemView), + default(string)); + + /// + /// The detail bindable property for the secondary text. + /// + public static readonly BindableProperty DetailProperty = BindableProperty.Create( + nameof(Detail), + typeof(string), + typeof(ReactiveImageItemView), + default(string)); + + /// + /// The text color bindable property. + /// + public static readonly BindableProperty TextColorProperty = BindableProperty.Create( + nameof(TextColor), + typeof(Color), + typeof(ReactiveImageItemView), + default(Color)); + + /// + /// The detail color bindable property. + /// + public static readonly BindableProperty DetailColorProperty = BindableProperty.Create( + nameof(DetailColor), + typeof(Color), + typeof(ReactiveImageItemView), + default(Color)); + + private readonly Image _image; + private readonly Label _textLabel; + private readonly Label _detailLabel; + + /// + /// Initializes a new instance of the class. + /// + public ReactiveImageItemView() + { + _image = new Image + { + WidthRequest = 40, + HeightRequest = 40, + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Start, + Aspect = Aspect.AspectFill + }; + + _textLabel = new Label + { + FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), + VerticalOptions = LayoutOptions.Center + }; + + _detailLabel = new Label + { + FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + VerticalOptions = LayoutOptions.Center, + Opacity = 0.7 + }; + + var textStackLayout = new StackLayout + { + Orientation = StackOrientation.Vertical, + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.FillAndExpand, + Children = { _textLabel, _detailLabel } + }; + + var mainStackLayout = new StackLayout + { + Orientation = StackOrientation.Horizontal, + VerticalOptions = LayoutOptions.Center, + Padding = new Thickness(16, 8), + Spacing = 12, + Children = { _image, textStackLayout } + }; + + Content = mainStackLayout; + + // Bind the control properties to the bindable properties + _image.SetBinding(Image.SourceProperty, new Binding(nameof(ImageSource), source: this)); + _textLabel.SetBinding(Label.TextProperty, new Binding(nameof(Text), source: this)); + _textLabel.SetBinding(Label.TextColorProperty, new Binding(nameof(TextColor), source: this)); + _detailLabel.SetBinding(Label.TextProperty, new Binding(nameof(Detail), source: this)); + _detailLabel.SetBinding(Label.TextColorProperty, new Binding(nameof(DetailColor), source: this)); + } + + /// + /// Gets or sets the image source to display. + /// + public ImageSource? ImageSource + { + get => (ImageSource?)GetValue(ImageSourceProperty); + set => SetValue(ImageSourceProperty, value); + } + + /// + /// Gets or sets the primary text to display. + /// + public string? Text + { + get => (string?)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + /// + /// Gets or sets the detail text to display. + /// + public string? Detail + { + get => (string?)GetValue(DetailProperty); + set => SetValue(DetailProperty, value); + } + + /// + /// Gets or sets the color of the primary text. + /// + public Color TextColor + { + get => (Color)GetValue(TextColorProperty); + set => SetValue(TextColorProperty, value); + } + + /// + /// Gets or sets the color of the detail text. + /// + public Color DetailColor + { + get => (Color)GetValue(DetailColorProperty); + set => SetValue(DetailColorProperty, value); + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs index b64079ca3c..25b5ce2263 100644 --- a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs +++ b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs @@ -17,6 +17,7 @@ namespace ReactiveUI.Maui; [RequiresDynamicCode("ReactiveSwitchCell uses methods that require dynamic code generation")] [RequiresUnreferencedCode("ReactiveSwitchCell uses methods that may require unreferenced code")] #endif +[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 ReactiveSwitchCell : SwitchCell, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI.Maui/ReactiveTextCell.cs b/src/ReactiveUI.Maui/ReactiveTextCell.cs index 924f3be4ee..18a02b8c54 100644 --- a/src/ReactiveUI.Maui/ReactiveTextCell.cs +++ b/src/ReactiveUI.Maui/ReactiveTextCell.cs @@ -17,6 +17,7 @@ namespace ReactiveUI.Maui; [RequiresDynamicCode("ReactiveTextCell uses methods that require dynamic code generation")] [RequiresUnreferencedCode("ReactiveTextCell uses methods that may require unreferenced code")] #endif +[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 : TextCell, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI.Maui/ReactiveTextItemView.cs b/src/ReactiveUI.Maui/ReactiveTextItemView.cs new file mode 100644 index 0000000000..b429d86ce8 --- /dev/null +++ b/src/ReactiveUI.Maui/ReactiveTextItemView.cs @@ -0,0 +1,133 @@ +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using Microsoft.Maui.Controls; + +namespace ReactiveUI.Maui; + +/// +/// A that displays text content similar to a TextCell, +/// but designed for use with CollectionView and DataTemplates. This serves as a modern replacement +/// for ReactiveTextCell which relied on the deprecated ListView. +/// +/// The type of the view model. +/// +#if NET6_0_OR_GREATER +[RequiresDynamicCode("ReactiveTextItemView uses methods that require dynamic code generation")] +[RequiresUnreferencedCode("ReactiveTextItemView uses methods that may require unreferenced code")] +#endif +public partial class ReactiveTextItemView : ReactiveContentView + where TViewModel : class +{ + /// + /// The text bindable property for the primary text. + /// + public static readonly BindableProperty TextProperty = BindableProperty.Create( + nameof(Text), + typeof(string), + typeof(ReactiveTextItemView), + default(string)); + + /// + /// The detail bindable property for the secondary text. + /// + public static readonly BindableProperty DetailProperty = BindableProperty.Create( + nameof(Detail), + typeof(string), + typeof(ReactiveTextItemView), + default(string)); + + /// + /// The text color bindable property. + /// + public static readonly BindableProperty TextColorProperty = BindableProperty.Create( + nameof(TextColor), + typeof(Color), + typeof(ReactiveTextItemView), + default(Color)); + + /// + /// The detail color bindable property. + /// + public static readonly BindableProperty DetailColorProperty = BindableProperty.Create( + nameof(DetailColor), + typeof(Color), + typeof(ReactiveTextItemView), + default(Color)); + + private readonly Label _textLabel; + private readonly Label _detailLabel; + + /// + /// Initializes a new instance of the class. + /// + public ReactiveTextItemView() + { + _textLabel = new Label + { + FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), + VerticalOptions = LayoutOptions.Center + }; + + _detailLabel = new Label + { + FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + VerticalOptions = LayoutOptions.Center, + Opacity = 0.7 + }; + + var stackLayout = new StackLayout + { + Orientation = StackOrientation.Vertical, + VerticalOptions = LayoutOptions.Center, + Padding = new Thickness(16, 8), + Children = { _textLabel, _detailLabel } + }; + + Content = stackLayout; + + // Bind the label properties to the bindable properties + _textLabel.SetBinding(Label.TextProperty, new Binding(nameof(Text), source: this)); + _textLabel.SetBinding(Label.TextColorProperty, new Binding(nameof(TextColor), source: this)); + _detailLabel.SetBinding(Label.TextProperty, new Binding(nameof(Detail), source: this)); + _detailLabel.SetBinding(Label.TextColorProperty, new Binding(nameof(DetailColor), source: this)); + } + + /// + /// Gets or sets the primary text to display. + /// + public string? Text + { + get => (string?)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + /// + /// Gets or sets the detail text to display. + /// + public string? Detail + { + get => (string?)GetValue(DetailProperty); + set => SetValue(DetailProperty, value); + } + + /// + /// Gets or sets the color of the primary text. + /// + public Color TextColor + { + get => (Color)GetValue(TextColorProperty); + set => SetValue(TextColorProperty, value); + } + + /// + /// Gets or sets the color of the detail text. + /// + public Color DetailColor + { + get => (Color)GetValue(DetailColorProperty); + set => SetValue(DetailColorProperty, value); + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Maui/ReactiveViewCell.cs b/src/ReactiveUI.Maui/ReactiveViewCell.cs index 7ef8eca87a..ffd33eb160 100644 --- a/src/ReactiveUI.Maui/ReactiveViewCell.cs +++ b/src/ReactiveUI.Maui/ReactiveViewCell.cs @@ -17,6 +17,7 @@ namespace ReactiveUI.Maui; [RequiresDynamicCode("ReactiveViewCell uses methods that require dynamic code generation")] [RequiresUnreferencedCode("ReactiveViewCell uses methods that may require unreferenced code")] #endif +[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 ReactiveViewCell : ViewCell, IViewFor where TViewModel : class { From 3fb0401ad3553cdb792b25c71e3137b99fe8f07d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Sep 2025 06:11:21 +0000 Subject: [PATCH 3/4] Fix using directives and remove deprecated Device.GetNamedSize API usage Co-authored-by: glennawatson <5834289+glennawatson@users.noreply.github.com> --- src/ReactiveUI.Maui/ReactiveEntryCell.cs | 1 + src/ReactiveUI.Maui/ReactiveImageCell.cs | 1 + src/ReactiveUI.Maui/ReactiveImageItemView.cs | 4 ++-- src/ReactiveUI.Maui/ReactiveSwitchCell.cs | 1 + src/ReactiveUI.Maui/ReactiveTextCell.cs | 1 + src/ReactiveUI.Maui/ReactiveTextItemView.cs | 4 ++-- src/ReactiveUI.Maui/ReactiveViewCell.cs | 1 + 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ReactiveUI.Maui/ReactiveEntryCell.cs b/src/ReactiveUI.Maui/ReactiveEntryCell.cs index f7aabcb45a..961c1e183f 100644 --- a/src/ReactiveUI.Maui/ReactiveEntryCell.cs +++ b/src/ReactiveUI.Maui/ReactiveEntryCell.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; diff --git a/src/ReactiveUI.Maui/ReactiveImageCell.cs b/src/ReactiveUI.Maui/ReactiveImageCell.cs index cefdd20f4f..0f4e8fa8f3 100644 --- a/src/ReactiveUI.Maui/ReactiveImageCell.cs +++ b/src/ReactiveUI.Maui/ReactiveImageCell.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; diff --git a/src/ReactiveUI.Maui/ReactiveImageItemView.cs b/src/ReactiveUI.Maui/ReactiveImageItemView.cs index ebd1f65506..fd9097b630 100644 --- a/src/ReactiveUI.Maui/ReactiveImageItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveImageItemView.cs @@ -86,13 +86,13 @@ public ReactiveImageItemView() _textLabel = new Label { - FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), + FontSize = 16, VerticalOptions = LayoutOptions.Center }; _detailLabel = new Label { - FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + FontSize = 12, VerticalOptions = LayoutOptions.Center, Opacity = 0.7 }; diff --git a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs index 25b5ce2263..4af0e3bdd1 100644 --- a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs +++ b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; diff --git a/src/ReactiveUI.Maui/ReactiveTextCell.cs b/src/ReactiveUI.Maui/ReactiveTextCell.cs index 18a02b8c54..34815ce447 100644 --- a/src/ReactiveUI.Maui/ReactiveTextCell.cs +++ b/src/ReactiveUI.Maui/ReactiveTextCell.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; diff --git a/src/ReactiveUI.Maui/ReactiveTextItemView.cs b/src/ReactiveUI.Maui/ReactiveTextItemView.cs index b429d86ce8..5c9477618d 100644 --- a/src/ReactiveUI.Maui/ReactiveTextItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveTextItemView.cs @@ -67,13 +67,13 @@ public ReactiveTextItemView() { _textLabel = new Label { - FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), + FontSize = 16, VerticalOptions = LayoutOptions.Center }; _detailLabel = new Label { - FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), + FontSize = 12, VerticalOptions = LayoutOptions.Center, Opacity = 0.7 }; diff --git a/src/ReactiveUI.Maui/ReactiveViewCell.cs b/src/ReactiveUI.Maui/ReactiveViewCell.cs index ffd33eb160..23315a55fc 100644 --- a/src/ReactiveUI.Maui/ReactiveViewCell.cs +++ b/src/ReactiveUI.Maui/ReactiveViewCell.cs @@ -3,6 +3,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; From 1719da4bcda41edee2613a75bbe5f05c23508aea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Sep 2025 06:41:06 +0000 Subject: [PATCH 4/4] Fix compile errors in ReactiveTextItemView and ReactiveImageItemView - add missing using directive and simplify layout properties Co-authored-by: glennawatson <5834289+glennawatson@users.noreply.github.com> --- .gitignore | 1 + src/ReactiveUI.Maui/ReactiveImageItemView.cs | 8 ++++---- src/ReactiveUI.Maui/ReactiveTextItemView.cs | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 2d94781984..9602fa91e5 100644 --- a/.gitignore +++ b/.gitignore @@ -449,3 +449,4 @@ src/Tools/ # MSBuild generator editor configs **/*.GeneratedMSBuildEditorConfig.editorconfig /app +.dotnet/ diff --git a/src/ReactiveUI.Maui/ReactiveImageItemView.cs b/src/ReactiveUI.Maui/ReactiveImageItemView.cs index fd9097b630..0f538e914c 100644 --- a/src/ReactiveUI.Maui/ReactiveImageItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveImageItemView.cs @@ -4,6 +4,7 @@ // See the LICENSE file in the project root for full license information. using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; namespace ReactiveUI.Maui; @@ -80,8 +81,7 @@ public ReactiveImageItemView() WidthRequest = 40, HeightRequest = 40, VerticalOptions = LayoutOptions.Center, - HorizontalOptions = LayoutOptions.Start, - Aspect = Aspect.AspectFill + HorizontalOptions = LayoutOptions.Start }; _textLabel = new Label @@ -101,7 +101,7 @@ public ReactiveImageItemView() { Orientation = StackOrientation.Vertical, VerticalOptions = LayoutOptions.Center, - HorizontalOptions = LayoutOptions.FillAndExpand, + HorizontalOptions = LayoutOptions.Fill, Children = { _textLabel, _detailLabel } }; @@ -109,7 +109,7 @@ public ReactiveImageItemView() { Orientation = StackOrientation.Horizontal, VerticalOptions = LayoutOptions.Center, - Padding = new Thickness(16, 8), + Padding = 16, Spacing = 12, Children = { _image, textStackLayout } }; diff --git a/src/ReactiveUI.Maui/ReactiveTextItemView.cs b/src/ReactiveUI.Maui/ReactiveTextItemView.cs index 5c9477618d..44f4fc4770 100644 --- a/src/ReactiveUI.Maui/ReactiveTextItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveTextItemView.cs @@ -4,6 +4,7 @@ // See the LICENSE file in the project root for full license information. using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; namespace ReactiveUI.Maui; @@ -82,7 +83,7 @@ public ReactiveTextItemView() { Orientation = StackOrientation.Vertical, VerticalOptions = LayoutOptions.Center, - Padding = new Thickness(16, 8), + Padding = 16, Children = { _textLabel, _detailLabel } };