Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
127ef49
Subscribe to itemslayout propertychanged in virtual view
bhavanesh2001 May 23, 2025
e2333ee
remove itemslayout property changed subscription from MauiRecyclerView
bhavanesh2001 May 23, 2025
aec1b5c
Remove ItemsLayout Propertychanged subscription in windows handler
bhavanesh2001 May 23, 2025
93d36ed
Windows remove duplicate calls
bhavanesh2001 May 24, 2025
75f769a
fix failing tests on windows
bhavanesh2001 May 26, 2025
0b10f3c
fix iOS CV1
bhavanesh2001 May 26, 2025
6cafe07
enable memory tests for CV2
bhavanesh2001 May 26, 2025
c40f99a
Implement Command mapper for notifying ItemsLayout Property Changes
bhavanesh2001 May 27, 2025
7a58993
Remove extra handler constructor on android
bhavanesh2001 May 27, 2025
4f9a379
undo CarouselViewHandler changes
bhavanesh2001 May 27, 2025
02fdd38
improove android command mapper
bhavanesh2001 May 27, 2025
16012c8
improove windows command mapper
bhavanesh2001 May 27, 2025
33341cb
fix formatting
bhavanesh2001 May 28, 2025
0243159
update memory tests
bhavanesh2001 May 28, 2025
c97045c
CarouselView implementation
bhavanesh2001 May 29, 2025
cf4a025
make stuff internal for now
bhavanesh2001 May 29, 2025
b01aca3
pass command mapper to base
bhavanesh2001 May 29, 2025
535706f
fix ios carouselview1
bhavanesh2001 May 29, 2025
d3cf8bb
fix failing test
bhavanesh2001 May 29, 2025
7d05163
dont change decalring type for now
bhavanesh2001 May 30, 2025
5ab035d
pass command mapper in cv2
bhavanesh2001 May 30, 2025
b34e5d5
spacing
bhavanesh2001 Jun 30, 2025
55b94e1
add UI tests
bhavanesh2001 Jul 10, 2025
3cb1b34
address pr feedback
bhavanesh2001 Jul 10, 2025
9e6054e
change ItemsLayoutProperty declaring type to CarouselView
bhavanesh2001 Jul 10, 2025
cb4076f
add snapshots and update uitest
bhavanesh2001 Jul 15, 2025
140810d
Add more snapshots
bhavanesh2001 Jul 21, 2025
163ce96
fix failing tests
bhavanesh2001 Aug 20, 2025
f75eeef
make mappings private and use IMauiRecyclerViewWithUpdates
bhavanesh2001 Sep 12, 2025
5f57b5e
Address review comments
bhavanesh2001 Sep 16, 2025
5d3b37c
Add stuff in PublicAPI files
bhavanesh2001 Sep 17, 2025
7b29309
Update command mapper names
bhavanesh2001 Sep 19, 2025
840c063
Updated snapshots
jsuarezruiz Sep 23, 2025
7fd7298
Added Issue 31071 repro sample
jsuarezruiz Sep 23, 2025
dbb3e5d
Added more tests
jsuarezruiz Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#nullable disable
using System.ComponentModel;

namespace Microsoft.Maui.Controls.Handlers.Items
{
public interface IMauiRecyclerView<TItemsView> where TItemsView : ItemsView
Expand Down Expand Up @@ -31,4 +33,10 @@ public interface IMauiRecyclerView<TItemsView> where TItemsView : ItemsView

public void UpdateCanReorderItems();
}

internal interface IMauiRecyclerViewWithUpdates<TItemsView> : IMauiRecyclerView<TItemsView> where TItemsView : ItemsView
{
void UpdateItemsLayoutProperties(PropertyChangedEventArgs args);
}

}
36 changes: 13 additions & 23 deletions src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace Microsoft.Maui.Controls.Handlers.Items
{

public class MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource> : RecyclerView, IMauiRecyclerView<TItemsView>
public class MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource> : RecyclerView, IMauiRecyclerViewWithUpdates<TItemsView>
where TItemsView : ItemsView
where TAdapter : ItemsViewAdapter<TItemsView, TItemsViewSource>
where TItemsViewSource : IItemsViewSource
Expand Down Expand Up @@ -43,10 +43,9 @@ public class MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource> : Recycler

ItemTouchHelper _itemTouchHelper;
SimpleItemTouchHelperCallback _itemTouchHelperCallback;
WeakNotifyPropertyChangedProxy _layoutPropertyChangedProxy;
PropertyChangedEventHandler _layoutPropertyChanged;

~MauiRecyclerView() => _layoutPropertyChangedProxy?.Unsubscribe();
//TODO: Remove this in .NET 10
~MauiRecyclerView() { }

public MauiRecyclerView(Context context, Func<IItemsLayout> getItemsLayout, Func<TAdapter> getAdapter) : base(new ContextThemeWrapper(context, Resource.Style.collectionViewTheme))
{
Expand All @@ -59,13 +58,6 @@ public MauiRecyclerView(Context context, Func<IItemsLayout> getItemsLayout, Func

public virtual void TearDownOldElement(TItemsView oldElement)
{
// Stop listening for layout property changes
if (_layoutPropertyChangedProxy is not null)
{
_layoutPropertyChangedProxy.Unsubscribe();
_layoutPropertyChanged = null;
}

// Stop listening for ScrollTo requests
oldElement.ScrollToRequested -= ScrollToRequested;

Expand Down Expand Up @@ -292,17 +284,9 @@ public virtual void UpdateLayoutManager()
{
return;
}

_layoutPropertyChangedProxy?.Unsubscribe();

ItemsLayout = itemsLayout;

// Keep track of the ItemsLayout's property changes
if (ItemsLayout is not null)
{
_layoutPropertyChanged ??= LayoutPropertyChanged;
_layoutPropertyChangedProxy = new WeakNotifyPropertyChangedProxy(ItemsLayout, _layoutPropertyChanged);
}

SetLayoutManager(SelectLayoutManager(ItemsLayout));

UpdateFlowDirection();
Expand Down Expand Up @@ -512,20 +496,26 @@ protected virtual void ScrollToRequested(object sender, ScrollToRequestEventArgs
ScrollTo(args);
}

//TODO: Remove this in .NET 10
protected virtual void LayoutPropertyChanged(object sender, PropertyChangedEventArgs propertyChanged)
{
if (propertyChanged.Is(GridItemsLayout.SpanProperty))
}

void IMauiRecyclerViewWithUpdates<TItemsView>.UpdateItemsLayoutProperties(PropertyChangedEventArgs args)
{

if (args.Is(GridItemsLayout.SpanProperty))
{
if (GetLayoutManager() is GridLayoutManager gridLayoutManager)
{
gridLayoutManager.SpanCount = ((GridItemsLayout)ItemsLayout).Span;
}
}
else if (propertyChanged.IsOneOf(Microsoft.Maui.Controls.ItemsLayout.SnapPointsTypeProperty, Microsoft.Maui.Controls.ItemsLayout.SnapPointsAlignmentProperty))
else if (args.IsOneOf(Microsoft.Maui.Controls.ItemsLayout.SnapPointsTypeProperty, Microsoft.Maui.Controls.ItemsLayout.SnapPointsAlignmentProperty))
{
UpdateSnapBehavior();
}
else if (propertyChanged.IsOneOf(LinearItemsLayout.ItemSpacingProperty,
else if (args.IsOneOf(LinearItemsLayout.ItemSpacingProperty,
GridItemsLayout.HorizontalItemSpacingProperty, GridItemsLayout.VerticalItemSpacingProperty))
{
UpdateItemSpacing();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable disable
using System.ComponentModel;
using Android.Views;
using AndroidX.RecyclerView.Widget;
using Microsoft.Maui.Graphics;
Expand Down Expand Up @@ -62,6 +63,15 @@ internal static void MapItemsLayout(CarouselViewHandler handler, CarouselView ca
}
}

static void MapItemsLayoutPropertyChanged(CarouselViewHandler handler, CarouselView view, object args)
{
if (handler.PlatformView is IMauiRecyclerViewWithUpdates<CarouselView> recyclerView &&
args is PropertyChangedEventArgs propertyChanged)
{
recyclerView.UpdateItemsLayoutProperties(propertyChanged);
}
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
_widthConstraint = widthConstraint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ public static void MapIsBounceEnabled(CarouselViewHandler handler, CarouselView
public static void MapIsSwipeEnabled(CarouselViewHandler handler, CarouselView carouselView) { }
public static void MapPeekAreaInsets(CarouselViewHandler handler, CarouselView carouselView) { }
public static void MapLoop(CarouselViewHandler handler, CarouselView carouselView) { }
static void MapItemsLayoutPropertyChanged(CarouselViewHandler handler, CarouselView view, object args){ }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,10 @@ public static void MapItemsLayout(CarouselViewHandler handler, CarouselView item
{
(handler.PlatformView as MauiCarouselView)?.UpdateLayoutManager();
}

static void MapItemsLayoutPropertyChanged(CarouselViewHandler handler, CarouselView view, object args)
{
(handler.PlatformView as MauiCarouselView)?.UpdateLayoutManager();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ public static void MapLoop(CarouselViewHandler handler, CarouselView carouselVie
handler.UpdateLoop();
}

static void MapItemsLayoutPropertyChanged(CarouselViewHandler handler, CarouselView view, object args)
{

}

internal bool InitialPositionSet { get; private set; }


Expand Down
12 changes: 10 additions & 2 deletions src/Controls/src/Core/Handlers/Items/CarouselViewHandler.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#nullable disable
using System;

namespace Microsoft.Maui.Controls.Handlers.Items
{
public partial class CarouselViewHandler
{
public CarouselViewHandler() : base(Mapper)
public CarouselViewHandler() : base(Mapper,CommandMapper)
{

}
public CarouselViewHandler(PropertyMapper mapper = null) : base(mapper ?? Mapper)
public CarouselViewHandler(PropertyMapper mapper = null) : base(mapper ?? Mapper, CommandMapper)
{

}
Expand All @@ -23,5 +25,11 @@ public CarouselViewHandler(PropertyMapper mapper = null) : base(mapper ?? Mapper
[Controls.CarouselView.PositionProperty.PropertyName] = MapPosition,
[Controls.CarouselView.CurrentItemProperty.PropertyName] = MapCurrentItem
};

//TODO Make this public in .NET10

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we could retarget to the net10.0 branch, and remove this TODOs, but I think not is too late.

internal static CommandMapper<CarouselView, CarouselViewHandler> CommandMapper = new(ViewCommandMapper)
{
["ItemsLayoutProperties"] = MapItemsLayoutPropertyChanged
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,10 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra

return size;
}

static void MapItemsLayoutPropertyChanged(CarouselViewHandler handler, CarouselView view, object args)
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ protected virtual void UpdateItemsLayout()
_defaultVerticalScrollVisibility = null;

UpdateItemTemplate();
UpdateItemsSource();
UpdateScrollBarVisibility();
UpdateEmptyView();
}
Expand Down
8 changes: 8 additions & 0 deletions src/Controls/src/Core/Handlers/Items/ItemsViewHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ public ItemsViewHandler(PropertyMapper mapper = null) : base(mapper ?? ItemsView

}

#if !ANDROID
//TODO Make this public in .NET10
internal ItemsViewHandler(PropertyMapper mapper = null, CommandMapper commandMapper = null) : base(mapper ?? ItemsViewMapper,commandMapper)
{

}
#endif

public static PropertyMapper<TItemsView, ItemsViewHandler<TItemsView>> ItemsViewMapper = new(ViewMapper)
{
[Controls.ItemsView.ItemsSourceProperty.PropertyName] = MapItemsSource,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using AndroidX.RecyclerView.Widget;

Expand All @@ -25,5 +26,15 @@ public static void MapItemsLayout(StructuredItemsViewHandler<TItemsView> handler

public static void MapItemSizingStrategy(StructuredItemsViewHandler<TItemsView> handler, StructuredItemsView itemsView)
=> (handler.PlatformView as IMauiRecyclerView<TItemsView>)?.UpdateAdapter();

static void MapItemsLayoutPropertyChanged(StructuredItemsViewHandler<TItemsView> handler, TItemsView view, object args)
{
if(args is not PropertyChangedEventArgs propertyChanged)
{
return;
}

(handler.PlatformView as IMauiRecyclerViewWithUpdates<TItemsView>)?.UpdateItemsLayoutProperties(propertyChanged);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public static void MapItemsLayout(StructuredItemsViewHandler<TItemsView> handler
public static void MapItemSizingStrategy(StructuredItemsViewHandler<TItemsView> handler, StructuredItemsView itemsView)
{
}

static void MapItemsLayoutPropertyChanged(StructuredItemsViewHandler<TItemsView> handler, TItemsView view, object args)
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,10 @@ public static void MapHeader(StructuredItemsViewHandler<TItemsView> handler, Str
{
(handler.PlatformView as MauiCollectionView<TItemsView>)?.UpdateAdaptor();
}

static void MapItemsLayoutPropertyChanged(StructuredItemsViewHandler<TItemsView> handler, TItemsView view, object args)
{
(handler.PlatformView as MauiCollectionView<TItemsView>)?.UpdateLayoutManager();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,47 @@ public partial class StructuredItemsViewHandler<TItemsView> : ItemsViewHandler<T
{
View _currentHeader;
View _currentFooter;
WeakNotifyPropertyChangedProxy _layoutPropertyChangedProxy;
PropertyChangedEventHandler _layoutPropertyChanged;
const string ListViewItemStyleKey = "DefaultListViewItemStyle";
const string GridViewItemStyleKey = "DefaultGridViewItemStyle";
static WStyle _listViewItemStyle;
static WStyle _gridViewItemStyle;

~StructuredItemsViewHandler() => _layoutPropertyChangedProxy?.Unsubscribe();
//TODO: Remove this in .NET 10
~StructuredItemsViewHandler() { }

protected override IItemsLayout Layout { get => ItemsView?.ItemsLayout; }

//TODO: Remove this in .NET 10
protected override void ConnectHandler(ListViewBase platformView)
{
base.ConnectHandler(platformView);

if (Layout is not null)
{
_layoutPropertyChanged ??= LayoutPropertyChanged;
_layoutPropertyChangedProxy = new WeakNotifyPropertyChangedProxy(Layout, _layoutPropertyChanged);
}
else if (_layoutPropertyChangedProxy is not null)
{
_layoutPropertyChangedProxy.Unsubscribe();
_layoutPropertyChangedProxy = null;
}
}

//TODO: Remove this in .NET 10
protected override void DisconnectHandler(ListViewBase platformView)
{
base.DisconnectHandler(platformView);
}

if (_layoutPropertyChangedProxy is not null)
void UpdateItemsLayoutProperties(object args)
{
if (args is not PropertyChangedEventArgs e)
{
_layoutPropertyChangedProxy.Unsubscribe();
_layoutPropertyChangedProxy = null;
return;
}
}

void LayoutPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == GridItemsLayout.SpanProperty.PropertyName)
{
UpdateItemsLayoutSpan();
}
else if (e.PropertyName == GridItemsLayout.HorizontalItemSpacingProperty.PropertyName || e.PropertyName == GridItemsLayout.VerticalItemSpacingProperty.PropertyName)
{
UpdateItemsLayoutItemSpacing();
}
else if (e.PropertyName == LinearItemsLayout.ItemSpacingProperty.PropertyName)
{
UpdateItemsLayoutItemSpacing();
}
}

public static void MapHeaderTemplate(StructuredItemsViewHandler<TItemsView> handler, StructuredItemsView itemsView)
Expand All @@ -79,6 +74,11 @@ public static void MapItemsLayout(StructuredItemsViewHandler<TItemsView> handler
handler.UpdateItemsLayout();
}

static void MapItemsLayoutPropertyChanged(StructuredItemsViewHandler<TItemsView> handler, TItemsView view, object args)
{
handler.UpdateItemsLayoutProperties(args);
}

public static void MapItemSizingStrategy(StructuredItemsViewHandler<TItemsView> handler, StructuredItemsView itemsView)
{

Expand Down Expand Up @@ -325,13 +325,13 @@ void UpdateItemsLayoutItemSpacing()

if (Layout is LinearItemsLayout linearItemsLayout)
{
switch (ListViewBase)
switch (linearItemsLayout.Orientation)
{
case FormsListView formsListView:
formsListView.ItemContainerStyle = GetVerticalItemContainerStyle(linearItemsLayout);
case ItemsLayoutOrientation.Vertical:
ListViewBase.ItemContainerStyle = GetVerticalItemContainerStyle(linearItemsLayout);
break;
case WListView listView:
listView.ItemContainerStyle = GetHorizontalItemContainerStyle(linearItemsLayout);
case ItemsLayoutOrientation.Horizontal:
ListViewBase.ItemContainerStyle = GetHorizontalItemContainerStyle(linearItemsLayout);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ namespace Microsoft.Maui.Controls.Handlers.Items
{
public partial class StructuredItemsViewHandler<TItemsView> where TItemsView : StructuredItemsView
{
public StructuredItemsViewHandler() : base(StructuredItemsViewMapper)
public StructuredItemsViewHandler() : base(StructuredItemsViewMapper, StructuredItemsViewCommandMapper)
{

}
public StructuredItemsViewHandler(PropertyMapper mapper = null) : base(mapper ?? StructuredItemsViewMapper)
public StructuredItemsViewHandler(PropertyMapper mapper = null) : base(mapper ?? StructuredItemsViewMapper,StructuredItemsViewCommandMapper)
{

}
Expand All @@ -33,5 +33,11 @@ public StructuredItemsViewHandler(PropertyMapper mapper = null) : base(mapper ??
[StructuredItemsView.ItemSizingStrategyProperty.PropertyName] = MapItemSizingStrategy
};

//TODO Make this public in .NET10
internal static CommandMapper<TItemsView, StructuredItemsViewHandler<TItemsView>> StructuredItemsViewCommandMapper = new(ViewCommandMapper)
{
["ItemsLayoutProperties"] = MapItemsLayoutPropertyChanged
};

}
}
Loading
Loading