Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 1 addition & 4 deletions src/Controls/src/Core/Binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,7 @@ internal override void Unapply(bool fromBindingContextChanged = false)

base.Unapply(fromBindingContextChanged: fromBindingContextChanged);

if (_expression != null)
{
_expression.Unapply();
}
_expression?.Unapply();
}
}
}
2 changes: 1 addition & 1 deletion src/Controls/src/Core/CollectionSynchronizationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal CollectionSynchronizationContext(object context, CollectionSynchronizat

internal object Context
{
get { return ContextReference != null ? ContextReference.Target : null; }
get { return ContextReference?.Target; }
}

internal WeakReference ContextReference { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
if (ChildCount > 0)
{
var platformView = GetChildAt(0);
if (platformView != null)
{
platformView.Layout(0, 0, r - l, b - t);
}
platformView?.Layout(0, 0, r - l, b - t);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void ITextWatcher.OnTextChanged(ICharSequence s, int start, int before, int coun
{
Action<string> changed = TextChanged;
if (changed != null)
changed(s != null ? s.ToString() : null);
changed(s?.ToString());
}

public void SetLabelTextColor(Color color, int defaultColorResourceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,7 @@ void UpdateTabTitle(ShellContent shellContent)
if (index >= 0)
{
var tab = _tablayout.GetTabAt(index);
if (tab != null)
{
tab.SetText(new string(shellContent.Title));
}
tab?.SetText(new string(shellContent.Title));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,7 @@ protected override void UpdateItemSpacing()

var adapter = GetAdapter();

if (adapter != null)
{
adapter.NotifyItemChanged(_oldPosition);
}
adapter?.NotifyItemChanged(_oldPosition);

base.UpdateItemSpacing();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Microsoft.Maui.Controls.Platform;
using Microsoft.UI.Xaml;
Expand All @@ -27,10 +28,16 @@ public partial class CarouselViewHandler : ItemsViewHandler<CarouselView>
bool _isCarouselViewReady;
NotifyCollectionChangedEventHandler _collectionChanged;
readonly WeakNotifyCollectionChangedProxy _proxy = new();
WeakNotifyPropertyChangedProxy _layoutPropertyChangedProxy;
PropertyChangedEventHandler _layoutPropertyChanged;

~CarouselViewHandler() => _proxy.Unsubscribe();
~CarouselViewHandler()
{
_proxy.Unsubscribe();
_layoutPropertyChangedProxy?.Unsubscribe();
}

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

LinearItemsLayout CarouselItemsLayout => ItemsView?.ItemsLayout;
WDataTemplate CarouselItemsViewTemplate => (WDataTemplate)WApp.Current.Resources["CarouselItemsViewDefaultTemplate"];
Expand All @@ -42,6 +49,8 @@ protected override void ConnectHandler(ListViewBase platformView)

UpdateScrollBarVisibilityForLoop();

UpdateLayoutPropertyChangeProxy();

base.ConnectHandler(platformView);
}

Expand All @@ -56,6 +65,12 @@ protected override void DisconnectHandler(ListViewBase platformView)
_proxy.Unsubscribe();
}

if (_layoutPropertyChangedProxy is not null)
{
_layoutPropertyChangedProxy.Unsubscribe();
_layoutPropertyChangedProxy = null;
}

if (_scrollViewer != null)
{
_scrollViewer.ViewChanging -= OnScrollViewChanging;
Expand All @@ -74,6 +89,10 @@ protected override void UpdateItemsSource()
return;

base.UpdateItemsSource();

// Update snap points after items source changes
UpdateSnapPointsType();
UpdateSnapPointsAlignment();
}

protected override void UpdateItemTemplate()
Expand Down Expand Up @@ -102,6 +121,10 @@ protected override void OnScrollViewerFound(ScrollViewer scrollViewer)
{
UpdateScrollBarVisibility();
}

// Update snap points when ScrollViewer is found
UpdateSnapPointsType();
UpdateSnapPointsAlignment();
}

protected override ICollectionView GetCollectionView(CollectionViewSource collectionViewSource)
Expand Down Expand Up @@ -433,16 +456,66 @@ WSnapPointsAlignment GetWindowsSnapPointsAlignment(SnapPointsAlignment snapPoint
return WSnapPointsAlignment.Center;
}

void LayoutPropertyChanged(object sender, PropertyChangedEventArgs e)
Copy link
Owner

Choose a reason for hiding this comment

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

PropertyChanged is never triggered

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in f760daa. The issue was that Windows handler was missing the MapItemsLayout method, so when ItemsLayout property changed, the PropertyChanged proxy wasn't updated. Added MapItemsLayout method and UpdateLayoutPropertyChangeProxy method to properly handle ItemsLayout changes and ensure PropertyChanged events trigger correctly.

Copy link
Owner

Choose a reason for hiding this comment

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

When scrolling the carouselview , the SnapPoint does not respond, it scrolls more than one item

Copy link
Author

Choose a reason for hiding this comment

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

Fixed in a76625e. The issue was that snap points were only being set during initial setup but not properly refreshed during the CarouselView lifecycle. Added snap points updates when ScrollViewer is found, when items change, and ensured proper Windows ScrollViewer configuration (disabled zoom mode, compatible scroll mode). This ensures snap points work correctly during scrolling and prevent scrolling past multiple items.

{
if (e.PropertyName == ItemsLayout.SnapPointsTypeProperty.PropertyName)
UpdateSnapPointsType();
else if (e.PropertyName == ItemsLayout.SnapPointsAlignmentProperty.PropertyName)
UpdateSnapPointsAlignment();
}

public static void MapItemsLayout(CarouselViewHandler handler, CarouselView carouselView)
{
handler.UpdateLayoutPropertyChangeProxy();
}

void UpdateLayoutPropertyChangeProxy()
{
// Clean up the old proxy
if (_layoutPropertyChangedProxy is not null)
{
_layoutPropertyChangedProxy.Unsubscribe();
_layoutPropertyChangedProxy = null;
}

// Set up the new proxy if Layout is not null
if (Layout is not null)
{
_layoutPropertyChanged ??= LayoutPropertyChanged;
_layoutPropertyChangedProxy = new WeakNotifyPropertyChangedProxy(Layout, _layoutPropertyChanged);
}
}

void UpdateSnapPointsType()
{
if (_scrollViewer == null || CarouselItemsLayout == null)
return;

var windowsSnapType = GetWindowsSnapPointsType(CarouselItemsLayout.SnapPointsType);

if (CarouselItemsLayout.Orientation == ItemsLayoutOrientation.Horizontal)
_scrollViewer.HorizontalSnapPointsType = GetWindowsSnapPointsType(CarouselItemsLayout.SnapPointsType);
{
_scrollViewer.HorizontalSnapPointsType = windowsSnapType;
// Ensure zoom mode is disabled for proper snap point behavior
_scrollViewer.ZoomMode = Microsoft.UI.Xaml.Controls.ZoomMode.Disabled;
// Ensure scroll mode is enabled for snap points to work
if (windowsSnapType != WSnapPointsType.None && ItemsView.IsSwipeEnabled)
{
_scrollViewer.HorizontalScrollMode = WScrollMode.Auto;
}
}

if (CarouselItemsLayout.Orientation == ItemsLayoutOrientation.Vertical)
_scrollViewer.VerticalSnapPointsType = GetWindowsSnapPointsType(CarouselItemsLayout.SnapPointsType);
{
_scrollViewer.VerticalSnapPointsType = windowsSnapType;
// Ensure zoom mode is disabled for proper snap point behavior
_scrollViewer.ZoomMode = Microsoft.UI.Xaml.Controls.ZoomMode.Disabled;
// Ensure scroll mode is enabled for snap points to work
if (windowsSnapType != WSnapPointsType.None && ItemsView.IsSwipeEnabled)
{
_scrollViewer.VerticalScrollMode = WScrollMode.Auto;
}
}
}

void UpdateSnapPointsAlignment()
Expand Down Expand Up @@ -596,6 +669,10 @@ void InvalidateItemSize()
item.ItemWidth = itemWidth;
}
ListViewBase.InvalidateMeasure();

// Refresh snap points after item size changes
UpdateSnapPointsType();
UpdateSnapPointsAlignment();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public CarouselViewHandler(PropertyMapper mapper = null) : base(mapper ?? Mapper

public static PropertyMapper<CarouselView, CarouselViewHandler> Mapper = new(ItemsViewMapper)
{
#if TIZEN || ANDROID
#if TIZEN || ANDROID || WINDOWS
[Controls.CarouselView.ItemsLayoutProperty.PropertyName] = MapItemsLayout,
#endif
[Controls.CarouselView.IsSwipeEnabledProperty.PropertyName] = MapIsSwipeEnabled,
Expand Down
3 changes: 1 addition & 2 deletions src/Controls/src/Core/Handlers/Shell/Tizen/ShellView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ public void UpdateBackgroundColor(GColor color)

public void UpdateCurrentItem(ShellItem newItem)
{
if (_currentItemHandler != null)
_currentItemHandler.Dispose();
_currentItemHandler?.Dispose();

if (newItem != null)
{
Expand Down
6 changes: 2 additions & 4 deletions src/Controls/src/Core/ImageSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ private set
{
if (_cancellationTokenSource == value)
return;
if (_cancellationTokenSource != null)
_cancellationTokenSource.Cancel();
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = value;
}
}
Expand Down Expand Up @@ -127,8 +126,7 @@ private protected async Task OnLoadingCompleted(bool cancelled)
return;

TaskCompletionSource<bool> tcs = Interlocked.Exchange(ref _completionSource, null);
if (tcs != null)
tcs.SetResult(cancelled);
tcs?.SetResult(cancelled);

await _cancellationTokenSourceLock.WaitAsync();
try
Expand Down
15 changes: 5 additions & 10 deletions src/Controls/src/Core/ListProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,13 @@ public void Clear()
if (_enumerator != null)
{
var dispose = _enumerator as IDisposable;
if (dispose != null)
dispose.Dispose();
dispose?.Dispose();

_enumerator = null;
}

if (_items != null)
_items.Clear();
if (_indexesCounted != null)
_indexesCounted.Clear();
_items?.Clear();
_indexesCounted?.Clear();

OnCountChanged();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Expand Down Expand Up @@ -303,8 +300,7 @@ bool TryGetValue(int index, out object value)
_windowIndex = 0;

var dispose = _enumerator as IDisposable;
if (dispose != null)
dispose.Dispose();
dispose?.Dispose();

_enumerator = null;
_enumeratorIndex = 0;
Expand Down Expand Up @@ -335,8 +331,7 @@ bool TryGetValue(int index, out object value)
if (!moved)
{
var dispose = _enumerator as IDisposable;
if (dispose != null)
dispose.Dispose();
dispose?.Dispose();

_enumerator = null;
_enumeratorIndex = 0;
Expand Down
3 changes: 1 addition & 2 deletions src/Controls/src/Core/LockingSemaphore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public void Release()
else
++_currentCount;
}
if (toRelease != null)
toRelease.TrySetResult(true);
toRelease?.TrySetResult(true);
}

public Task WaitAsync(CancellationToken token)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,7 @@ public static void UpdateMenuItems(this AToolbar toolbar,
int toolBarItemCount = i;
while (toolBarItemCount < previousMenuItems.Count)
{
if (menu != null)
{
menu.RemoveItem(previousMenuItems[toolBarItemCount].ItemId);
}
menu?.RemoveItem(previousMenuItems[toolBarItemCount].ItemId);
previousMenuItems[toolBarItemCount].Dispose();
previousMenuItems.RemoveAt(toolBarItemCount);
}
Expand Down
3 changes: 1 addition & 2 deletions src/Controls/src/Core/Shapes/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ public override PathF GetPath()
{
var path = new PathF();

if (Data != null)
Data.AppendPath(path);
Data?.AppendPath(path);

return path;
}
Expand Down
3 changes: 1 addition & 2 deletions src/Controls/src/Core/TemplateBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ internal override void Unapply(bool fromBindingContextChanged = false)
{
base.Unapply(fromBindingContextChanged: fromBindingContextChanged);

if (_expression != null)
_expression.Unapply();
_expression?.Unapply();
}

void ApplyInner(Element templatedParent, BindableObject bindableObject, BindableProperty targetProperty)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Xunit;

namespace Microsoft.Maui.DeviceTests
{
public partial class CarouselViewSnapPointsTests
{
// Windows-specific test to verify that the ScrollViewer properties are actually set
[Fact]
public async Task CarouselViewSnapPointsAreAppliedToScrollViewerOnWindows()
{
SetupBuilder();

var carouselView = new CarouselView
{
ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
ItemTemplate = new DataTemplate(() => new Label())
};

var handler = await CreateHandlerAsync(carouselView);

// Allow time for the handler to initialize
await Task.Delay(200);

// Change the snap points type
carouselView.ItemsLayout.SnapPointsType = SnapPointsType.Mandatory;

// Wait for the property change to propagate
await Task.Delay(100);

// Verify the change was applied to the view model
Assert.Equal(SnapPointsType.Mandatory, carouselView.ItemsLayout.SnapPointsType);

// Change the snap points alignment
carouselView.ItemsLayout.SnapPointsAlignment = SnapPointsAlignment.Start;

// Wait for the property change to propagate
await Task.Delay(100);

// Verify the change was applied to the view model
Assert.Equal(SnapPointsAlignment.Start, carouselView.ItemsLayout.SnapPointsAlignment);
}
}
}
Loading