diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 552cd5df..fdd1e33b 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -17,7 +17,7 @@ jobs: env: Solution_Name: ./src/MauiReactor.Build.sln TemplatePack_Name: ./src/MauiReactor.TemplatePack/MauiReactor.TemplatePack.csproj - Version: 2.0.7-beta + Version: 2.0.8-beta steps: - name: Checkout diff --git a/samples/MauiReactor.TestApp/Pages/ListViewExtendedTestPage.cs b/samples/MauiReactor.TestApp/Pages/ListViewExtendedTestPage.cs index 6b293ed5..74e565a7 100644 --- a/samples/MauiReactor.TestApp/Pages/ListViewExtendedTestPage.cs +++ b/samples/MauiReactor.TestApp/Pages/ListViewExtendedTestPage.cs @@ -26,8 +26,8 @@ public override VisualNode Render() //NOTE: Header/Footer not working under .net 7 //https://github.com/dotnet/maui/issues/13560 //https://github.com/dotnet/maui/issues/12312 - .Header(new Label("Header")) - .Footer(new Label("Footer")) + .Header(Label("Header")) + .Footer(Label("Footer")) }; } @@ -38,38 +38,37 @@ private void OnSelectedItem(object? sender, MauiControls.SelectedItemChangedEven private ViewCell RenderGroup(GroupOfPerson person) { - return new ViewCell - { - new Label(person.Initial) - { - new MenuFlyout - { - new MenuFlyoutItem("MenuItem1") + return ViewCell( + [ + Label(person.Initial, + [ + MenuFlyout( + [ + MenuFlyoutItem("MenuItem1") .OnClicked(()=>OnClickMenuItem("MenuItem1")), - new MenuFlyoutItem("MenuItem2") + MenuFlyoutItem("MenuItem2") .OnClicked(()=>OnClickMenuItem("MenuItem2")), - new MenuFlyoutItem("MenuItem3") + MenuFlyoutItem("MenuItem3") .OnClicked(()=>OnClickMenuItem("MenuItem3")), - } - } + ]) + ]) .FontSize(14.0) .FontAttributes(MauiControls.FontAttributes.Bold) .Margin(5) .BackgroundColor(Colors.LightGray) - , - }; + ]); } private ViewCell RenderItem(Person person) { - return new ViewCell - { - new Label($"{person.FirstName} {person.LastName}") + return ViewCell( + [ + Label($"{person.FirstName} {person.LastName}") .FontSize(14.0) .FontAttributes(MauiControls.FontAttributes.Bold) .Padding(5) .VerticalTextAlignment(TextAlignment.Center) - }; + ]); } private void OnClickMenuItem(string title) diff --git a/samples/MauiReactor.TestApp/Pages/TestBug187.cs b/samples/MauiReactor.TestApp/Pages/TestBug187.cs new file mode 100644 index 00000000..ae838fd6 --- /dev/null +++ b/samples/MauiReactor.TestApp/Pages/TestBug187.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MauiReactor.TestApp.Models; + +namespace MauiReactor.TestApp.Pages; + +public class TestBug187 : Component +{ + private readonly IEnumerable _allMonkeys = Monkey.GetList(); + + public override VisualNode Render() => ContentPage([ + new ListView(MauiControls.ListViewCachingStrategy.RetainElement) + .HasUnevenRows(true) + .ItemsSource(_allMonkeys, RenderMonkeyTemplate) + ]); + + private ViewCell RenderMonkeyTemplate(Monkey monkey) => monkey.Name switch + { + "Capuchin Monkey" or "Howler Monkey" or "Red-shanked Douc" => RenderSpecialMonkeyTemplate2(monkey), + _ => RenderFullMonkeyTemplate2(monkey) + }; + + private ViewCell RenderSpecialMonkeyTemplate(Monkey monkey) => new ViewCell + { + new HorizontalStackLayout + { + new Label(monkey.Name) + .FontSize(12.0) + .Margin(5) + } + .BackgroundColor(Colors.AliceBlue) + }; + + private ViewCell RenderSpecialMonkeyTemplate2(Monkey monkey) => ViewCell([ + HorizontalStackLayout( + [ + Label(monkey.Name) + .FontSize(12.0) + .Margin(5) + ]) + .BackgroundColor(Colors.AliceBlue) + ]); + + private ViewCell RenderFullMonkeyTemplate(Monkey monkey) => new ViewCell + { + new HorizontalStackLayout + { + new Image() + .Source(new Uri(monkey.ImageUrl)) + .HeightRequest(100) + .Margin(4), + + new StackLayout + { + new Label(monkey.Name) + .FontSize(12.0) + .Margin(5), + + new Label(monkey.Location) + .FontSize(12.0) + .Margin(5) + } + } + .Padding(10) + }; + + private ViewCell RenderFullMonkeyTemplate2(Monkey monkey) => ViewCell([ + HorizontalStackLayout( + [ + Image() + .Source(new Uri(monkey.ImageUrl)) + .HeightRequest(100) + .Margin(4), + + StackLayout( + [ + Label(monkey.Name) + .FontSize(12.0) + .Margin(5), + + Label(monkey.Location) + .FontSize(12.0) + .Margin(5) + ]) + ]) + .Padding(10) + ]); + +} diff --git a/src/MauiReactor/Internals/Validation.cs b/src/MauiReactor/Internals/Validation.cs index 1f3959e4..bfcf5731 100644 --- a/src/MauiReactor/Internals/Validation.cs +++ b/src/MauiReactor/Internals/Validation.cs @@ -6,12 +6,21 @@ public static class Validate { [return: NotNull] public static T EnsureNotNull([NotNull] this T? value) - => value ?? throw new InvalidOperationException(); + { + if (value == null) + { + throw new InvalidOperationException(); + } + + return value; + } public static void EnsureNull(this T? value) { if (value != null) + { throw new InvalidOperationException(); + } } } diff --git a/src/MauiReactor/Internals/VisualNodePool.cs b/src/MauiReactor/Internals/VisualNodePool.cs index c8de961d..45464f73 100644 --- a/src/MauiReactor/Internals/VisualNodePool.cs +++ b/src/MauiReactor/Internals/VisualNodePool.cs @@ -2,7 +2,8 @@ internal class VisualNodePool { - private readonly Stack _availableObjects = new(); + private readonly Stack _availableObjects = []; + private readonly HashSet _pooledObjects = []; public T GetObject() where T : VisualNode, new() { @@ -15,14 +16,19 @@ internal class VisualNodePool } else { - System.Diagnostics.Debug.WriteLine($"Generating new visual node: {typeof(T)}"); - return new T(); + //System.Diagnostics.Debug.WriteLine($"Generating new visual node: {typeof(T)}"); + var newInstance = new T(); + _pooledObjects.Add(newInstance); + return newInstance; } } public void ReleaseObject(VisualNode obj) { - //System.Diagnostics.Debug.WriteLine($"Releasing new visual node: {obj.GetType()}"); - _availableObjects.Push(obj); + if (_pooledObjects.Contains(obj)) + { + //System.Diagnostics.Debug.WriteLine($"Releasing new visual node: {obj.GetType()}"); + _availableObjects.Push(obj); + } } } \ No newline at end of file diff --git a/src/MauiReactor/PropertyValue.cs b/src/MauiReactor/PropertyValue.cs index d03793bf..8606b04d 100644 --- a/src/MauiReactor/PropertyValue.cs +++ b/src/MauiReactor/PropertyValue.cs @@ -15,10 +15,10 @@ public interface IPropertyValue public class PropertyValue : IPropertyValue { - private PropertyValue() - { - SetDefault = true; - } + //private PropertyValue() + //{ + // SetDefault = true; + //} public PropertyValue(T value) { Value = value; @@ -31,7 +31,7 @@ public PropertyValue(Func valueAction) Value = valueAction(); } - public static IPropertyValue Default { get; } = new PropertyValue(); + //public static IPropertyValue Default { get; } = new PropertyValue(); public T? Value { get; } diff --git a/src/MauiReactor/VisualNode.cs b/src/MauiReactor/VisualNode.cs index 7c6eb5d6..4ed20181 100644 --- a/src/MauiReactor/VisualNode.cs +++ b/src/MauiReactor/VisualNode.cs @@ -96,10 +96,7 @@ public static T WithMetadata(this T node, string key, object value) where T : public static T WithMetadata(this T node, object value) where T : VisualNode { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } + ArgumentNullException.ThrowIfNull(value); node.SetMetadata(value.GetType().FullName ?? throw new InvalidOperationException(), value); return node; @@ -120,7 +117,7 @@ public abstract class VisualNode : IVisualNode, IAutomationItemContainer protected internal Dictionary _animatables = []; - private readonly Dictionary _metadata = new(); + private readonly Dictionary _metadata = []; private IReadOnlyList? _children = null; @@ -214,21 +211,6 @@ internal virtual void Reset() public void AppendAnimatable(BindableProperty key, T animation, Action action) where T : RxAnimation { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (animation is null) - { - throw new ArgumentNullException(nameof(animation)); - } - - if (action is null) - { - throw new ArgumentNullException(nameof(action)); - } - var newAnimatableProperty = new Animatable(key, animation, new Action(target => action((T)target))); _animatables[key] = newAnimatableProperty; @@ -274,11 +256,11 @@ internal virtual bool Animate() { if (_isMounted) { - var animated = AnimateThis(); + var animated = AnimateThis(); - OnAnimate(); + OnAnimate(); - return animated; + return animated; } return false; @@ -541,7 +523,7 @@ protected virtual void OnMigrated(VisualNode newNode) _animatables.Clear(); - _isMounted = false; + //_isMounted = false; ReleaseNodeToPool(this); } @@ -643,7 +625,7 @@ IEnumerable IAutomationItemContainer.Descendants() static readonly Dictionary _nodePools = []; - protected T GetNodeFromPool() where T : VisualNode, new() + protected static T GetNodeFromPool() where T : VisualNode, new() { if (!_nodePools.TryGetValue(typeof(T), out var nodePool)) { @@ -654,7 +636,7 @@ IEnumerable IAutomationItemContainer.Descendants() } - private void ReleaseNodeToPool(VisualNode node) + private static void ReleaseNodeToPool(VisualNode node) { if (!_nodePools.TryGetValue(node.GetType(), out var nodePool)) { @@ -692,7 +674,7 @@ public static T Set(this T element, BindableProperty property, object value) { protected BindableObject? _nativeControl; - private readonly Dictionary _attachedProperties = new(); + private readonly Dictionary _attachedProperties = []; private Action? _componentRefAction; @@ -738,9 +720,12 @@ internal override void MergeWith(VisualNode newNode) ((VisualNode)newNode)._nativeControl = this._nativeControl; ((VisualNode)newNode)._isMounted = this._nativeControl != null; ((VisualNode)newNode)._componentRefAction?.Invoke(NativeControl); + OnMigrated(newNode); base.MergeWith(newNode); + + _nativeControl = null; } else { @@ -769,7 +754,7 @@ protected override void OnMigrated(VisualNode newNode) } _attachedProperties.Clear(); - _nativeControl = null; + //_nativeControl = null; base.OnMigrated(newNode); @@ -937,7 +922,7 @@ public static bool SetPropertyValue(this BindableObject dependencyObject, Bindab var propertiesBag = (HashSet?)dependencyObject.GetValue(VisualNode._mauiReactorPropertiesBagKey.BindableProperty); if (propertiesBag == null) { - dependencyObject.SetValue(VisualNode._mauiReactorPropertiesBagKey, propertiesBag = new HashSet()); + dependencyObject.SetValue(VisualNode._mauiReactorPropertiesBagKey, propertiesBag = []); } propertiesBag.Add(property);