CollectionView completed

This commit is contained in:
2026-01-17 01:18:57 +00:00
parent b1749b1347
commit 868ce1fcae
4 changed files with 789 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary>
/// Handler for CarouselView on Linux using Skia rendering.
/// Maps CarouselView to SkiaCarouselView platform view.
/// </summary>
public partial class CarouselViewHandler : ViewHandler<CarouselView, SkiaCarouselView>
{
private bool _isUpdatingPosition;
public static IPropertyMapper<CarouselView, CarouselViewHandler> Mapper =
new PropertyMapper<CarouselView, CarouselViewHandler>(ViewHandler.ViewMapper)
{
// ItemsView properties
[nameof(ItemsView.ItemsSource)] = MapItemsSource,
[nameof(ItemsView.ItemTemplate)] = MapItemTemplate,
[nameof(ItemsView.EmptyView)] = MapEmptyView,
// CarouselView specific properties
[nameof(CarouselView.Position)] = MapPosition,
[nameof(CarouselView.CurrentItem)] = MapCurrentItem,
[nameof(CarouselView.IsBounceEnabled)] = MapIsBounceEnabled,
[nameof(CarouselView.IsSwipeEnabled)] = MapIsSwipeEnabled,
[nameof(CarouselView.Loop)] = MapLoop,
[nameof(CarouselView.PeekAreaInsets)] = MapPeekAreaInsets,
[nameof(IView.Background)] = MapBackground,
};
public static CommandMapper<CarouselView, CarouselViewHandler> CommandMapper =
new(ViewHandler.ViewCommandMapper)
{
["ScrollTo"] = MapScrollTo,
};
public CarouselViewHandler() : base(Mapper, CommandMapper)
{
}
public CarouselViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaCarouselView CreatePlatformView()
{
return new SkiaCarouselView();
}
protected override void ConnectHandler(SkiaCarouselView platformView)
{
base.ConnectHandler(platformView);
platformView.PositionChanged += OnPositionChanged;
platformView.Scrolled += OnScrolled;
}
protected override void DisconnectHandler(SkiaCarouselView platformView)
{
platformView.PositionChanged -= OnPositionChanged;
platformView.Scrolled -= OnScrolled;
base.DisconnectHandler(platformView);
}
private void OnPositionChanged(object? sender, PositionChangedEventArgs e)
{
if (VirtualView is null || _isUpdatingPosition) return;
try
{
_isUpdatingPosition = true;
if (VirtualView.Position != e.CurrentPosition)
{
VirtualView.Position = e.CurrentPosition;
}
// Update CurrentItem
if (VirtualView.ItemsSource is System.Collections.IList list &&
e.CurrentPosition >= 0 && e.CurrentPosition < list.Count)
{
VirtualView.CurrentItem = list[e.CurrentPosition];
}
}
finally
{
_isUpdatingPosition = false;
}
}
private void OnScrolled(object? sender, EventArgs e)
{
// CarouselView doesn't have a direct Scrolled event in MAUI
// but we can use this for internal state tracking
}
public static void MapItemsSource(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null || handler.MauiContext is null) return;
handler.PlatformView.ClearItems();
var itemsSource = carouselView.ItemsSource;
if (itemsSource == null) return;
var template = carouselView.ItemTemplate;
foreach (var item in itemsSource)
{
SkiaView? skiaView = null;
if (template != null)
{
try
{
var content = template.CreateContent();
if (content is View view)
{
view.BindingContext = item;
if (view.Handler == null)
{
view.Handler = view.ToViewHandler(handler.MauiContext);
}
if (view.Handler?.PlatformView is SkiaView sv)
{
skiaView = sv;
}
}
}
catch
{
// Ignore template errors
}
}
if (skiaView == null)
{
// Create a simple label for the item
skiaView = new SkiaLabel { Text = item?.ToString() ?? "" };
}
handler.PlatformView.AddItem(skiaView);
}
handler.PlatformView.Invalidate();
}
public static void MapItemTemplate(CarouselViewHandler handler, CarouselView carouselView)
{
// Re-map items when template changes
MapItemsSource(handler, carouselView);
}
public static void MapEmptyView(CarouselViewHandler handler, CarouselView carouselView)
{
// CarouselView doesn't typically show empty view - handled by ItemsSource
}
public static void MapPosition(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null || handler._isUpdatingPosition) return;
try
{
handler._isUpdatingPosition = true;
if (handler.PlatformView.Position != carouselView.Position)
{
handler.PlatformView.Position = carouselView.Position;
}
}
finally
{
handler._isUpdatingPosition = false;
}
}
public static void MapCurrentItem(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null || handler._isUpdatingPosition) return;
// Find position of current item
if (carouselView.ItemsSource is System.Collections.IList list && carouselView.CurrentItem != null)
{
int index = list.IndexOf(carouselView.CurrentItem);
if (index >= 0 && index != handler.PlatformView.Position)
{
try
{
handler._isUpdatingPosition = true;
handler.PlatformView.Position = index;
}
finally
{
handler._isUpdatingPosition = false;
}
}
}
}
public static void MapIsBounceEnabled(CarouselViewHandler handler, CarouselView carouselView)
{
// SkiaCarouselView handles bounce internally
// Could add IsBounceEnabled property if needed
}
public static void MapIsSwipeEnabled(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.IsSwipeEnabled = carouselView.IsSwipeEnabled;
}
public static void MapLoop(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.Loop = carouselView.Loop;
}
public static void MapPeekAreaInsets(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null) return;
// PeekAreaInsets is a Thickness in MAUI, use Left for horizontal peek
handler.PlatformView.PeekAreaInsets = (float)carouselView.PeekAreaInsets.Left;
}
public static void MapBackground(CarouselViewHandler handler, CarouselView carouselView)
{
if (handler.PlatformView is null) return;
if (carouselView.Background is SolidColorBrush solidBrush)
{
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
}
}
public static void MapScrollTo(CarouselViewHandler handler, CarouselView carouselView, object? args)
{
if (handler.PlatformView is null) return;
if (args is ScrollToRequestEventArgs scrollArgs)
{
handler.PlatformView.ScrollTo(scrollArgs.Index, scrollArgs.IsAnimated);
}
}
}

View File

@@ -0,0 +1,157 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary>
/// Handler for IndicatorView on Linux using Skia rendering.
/// Maps IndicatorView to SkiaIndicatorView platform view.
/// </summary>
public partial class IndicatorViewHandler : ViewHandler<IndicatorView, SkiaIndicatorView>
{
private bool _isUpdatingPosition;
public static IPropertyMapper<IndicatorView, IndicatorViewHandler> Mapper =
new PropertyMapper<IndicatorView, IndicatorViewHandler>(ViewHandler.ViewMapper)
{
[nameof(IndicatorView.Count)] = MapCount,
[nameof(IndicatorView.Position)] = MapPosition,
[nameof(IndicatorView.IndicatorColor)] = MapIndicatorColor,
[nameof(IndicatorView.SelectedIndicatorColor)] = MapSelectedIndicatorColor,
[nameof(IndicatorView.IndicatorSize)] = MapIndicatorSize,
[nameof(IndicatorView.IndicatorsShape)] = MapIndicatorsShape,
[nameof(IndicatorView.MaximumVisible)] = MapMaximumVisible,
[nameof(IndicatorView.HideSingle)] = MapHideSingle,
[nameof(IndicatorView.ItemsSource)] = MapItemsSource,
};
public static CommandMapper<IndicatorView, IndicatorViewHandler> CommandMapper =
new(ViewHandler.ViewCommandMapper)
{
};
public IndicatorViewHandler() : base(Mapper, CommandMapper)
{
}
public IndicatorViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaIndicatorView CreatePlatformView()
{
return new SkiaIndicatorView();
}
protected override void ConnectHandler(SkiaIndicatorView platformView)
{
base.ConnectHandler(platformView);
// SkiaIndicatorView doesn't have position changed event, but we can add one if needed
}
protected override void DisconnectHandler(SkiaIndicatorView platformView)
{
base.DisconnectHandler(platformView);
}
public static void MapCount(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.Count = indicatorView.Count;
}
public static void MapPosition(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null || handler._isUpdatingPosition) return;
try
{
handler._isUpdatingPosition = true;
handler.PlatformView.Position = indicatorView.Position;
}
finally
{
handler._isUpdatingPosition = false;
}
}
public static void MapIndicatorColor(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
if (indicatorView.IndicatorColor is not null)
{
handler.PlatformView.IndicatorColor = indicatorView.IndicatorColor.ToSKColor();
}
}
public static void MapSelectedIndicatorColor(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
if (indicatorView.SelectedIndicatorColor is not null)
{
handler.PlatformView.SelectedIndicatorColor = indicatorView.SelectedIndicatorColor.ToSKColor();
}
}
public static void MapIndicatorSize(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.IndicatorSize = (float)indicatorView.IndicatorSize;
handler.PlatformView.SelectedIndicatorSize = (float)indicatorView.IndicatorSize;
}
public static void MapIndicatorsShape(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.IndicatorShape = indicatorView.IndicatorsShape switch
{
Controls.IndicatorShape.Circle => Platform.IndicatorShape.Circle,
Controls.IndicatorShape.Square => Platform.IndicatorShape.Square,
_ => Platform.IndicatorShape.Circle
};
}
public static void MapMaximumVisible(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.MaximumVisible = indicatorView.MaximumVisible;
}
public static void MapHideSingle(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.HideSingle = indicatorView.HideSingle;
}
public static void MapItemsSource(IndicatorViewHandler handler, IndicatorView indicatorView)
{
if (handler.PlatformView is null) return;
// Count items from ItemsSource
int count = 0;
if (indicatorView.ItemsSource is System.Collections.ICollection collection)
{
count = collection.Count;
}
else if (indicatorView.ItemsSource is System.Collections.IEnumerable enumerable)
{
foreach (var _ in enumerable)
{
count++;
}
}
handler.PlatformView.Count = count;
}
}

View File

@@ -0,0 +1,151 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary>
/// Handler for RefreshView on Linux using Skia rendering.
/// Maps RefreshView to SkiaRefreshView platform view.
/// </summary>
public partial class RefreshViewHandler : ViewHandler<RefreshView, SkiaRefreshView>
{
private bool _isUpdatingRefreshing;
public static IPropertyMapper<RefreshView, RefreshViewHandler> Mapper =
new PropertyMapper<RefreshView, RefreshViewHandler>(ViewHandler.ViewMapper)
{
[nameof(RefreshView.Content)] = MapContent,
[nameof(RefreshView.IsRefreshing)] = MapIsRefreshing,
[nameof(RefreshView.RefreshColor)] = MapRefreshColor,
[nameof(RefreshView.Command)] = MapCommand,
[nameof(RefreshView.CommandParameter)] = MapCommandParameter,
[nameof(IView.Background)] = MapBackground,
};
public static CommandMapper<RefreshView, RefreshViewHandler> CommandMapper =
new(ViewHandler.ViewCommandMapper)
{
};
public RefreshViewHandler() : base(Mapper, CommandMapper)
{
}
public RefreshViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaRefreshView CreatePlatformView()
{
return new SkiaRefreshView();
}
protected override void ConnectHandler(SkiaRefreshView platformView)
{
base.ConnectHandler(platformView);
platformView.Refreshing += OnRefreshing;
}
protected override void DisconnectHandler(SkiaRefreshView platformView)
{
platformView.Refreshing -= OnRefreshing;
base.DisconnectHandler(platformView);
}
private void OnRefreshing(object? sender, EventArgs e)
{
if (VirtualView is null || _isUpdatingRefreshing) return;
try
{
_isUpdatingRefreshing = true;
// Notify the virtual view that refreshing has started
VirtualView.IsRefreshing = true;
// The command will be executed by the platform view
}
finally
{
_isUpdatingRefreshing = false;
}
}
public static void MapContent(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null || handler.MauiContext is null) return;
var content = refreshView.Content;
if (content == null)
{
handler.PlatformView.Content = null;
return;
}
// Create handler for content
if (content.Handler == null)
{
content.Handler = content.ToViewHandler(handler.MauiContext);
}
if (content.Handler?.PlatformView is SkiaView skiaContent)
{
handler.PlatformView.Content = skiaContent;
}
}
public static void MapIsRefreshing(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null || handler._isUpdatingRefreshing) return;
try
{
handler._isUpdatingRefreshing = true;
handler.PlatformView.IsRefreshing = refreshView.IsRefreshing;
}
finally
{
handler._isUpdatingRefreshing = false;
}
}
public static void MapRefreshColor(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null) return;
if (refreshView.RefreshColor is not null)
{
handler.PlatformView.RefreshColor = refreshView.RefreshColor.ToSKColor();
}
}
public static void MapCommand(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.Command = refreshView.Command;
}
public static void MapCommandParameter(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.CommandParameter = refreshView.CommandParameter;
}
public static void MapBackground(RefreshViewHandler handler, RefreshView refreshView)
{
if (handler.PlatformView is null) return;
if (refreshView.Background is SolidColorBrush solidBrush)
{
handler.PlatformView.RefreshBackgroundColor = solidBrush.Color.ToSKColor();
}
}
}

View File

@@ -0,0 +1,226 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary>
/// Handler for SwipeView on Linux using Skia rendering.
/// Maps SwipeView to SkiaSwipeView platform view.
/// </summary>
public partial class SwipeViewHandler : ViewHandler<SwipeView, SkiaSwipeView>
{
public static IPropertyMapper<SwipeView, SwipeViewHandler> Mapper =
new PropertyMapper<SwipeView, SwipeViewHandler>(ViewHandler.ViewMapper)
{
[nameof(SwipeView.Content)] = MapContent,
[nameof(SwipeView.LeftItems)] = MapLeftItems,
[nameof(SwipeView.RightItems)] = MapRightItems,
[nameof(SwipeView.TopItems)] = MapTopItems,
[nameof(SwipeView.BottomItems)] = MapBottomItems,
[nameof(SwipeView.Threshold)] = MapThreshold,
[nameof(IView.Background)] = MapBackground,
};
public static CommandMapper<SwipeView, SwipeViewHandler> CommandMapper =
new(ViewHandler.ViewCommandMapper)
{
["RequestOpen"] = MapRequestOpen,
["RequestClose"] = MapRequestClose,
};
public SwipeViewHandler() : base(Mapper, CommandMapper)
{
}
public SwipeViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaSwipeView CreatePlatformView()
{
return new SkiaSwipeView();
}
protected override void ConnectHandler(SkiaSwipeView platformView)
{
base.ConnectHandler(platformView);
platformView.SwipeStarted += OnSwipeStarted;
platformView.SwipeEnded += OnSwipeEnded;
}
protected override void DisconnectHandler(SkiaSwipeView platformView)
{
platformView.SwipeStarted -= OnSwipeStarted;
platformView.SwipeEnded -= OnSwipeEnded;
base.DisconnectHandler(platformView);
}
private void OnSwipeStarted(object? sender, Platform.SwipeStartedEventArgs e)
{
// SwipeView events are handled internally by the platform view
}
private void OnSwipeEnded(object? sender, Platform.SwipeEndedEventArgs e)
{
// SwipeView events are handled internally by the platform view
}
public static void MapContent(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null || handler.MauiContext is null) return;
var content = swipeView.Content;
if (content == null)
{
handler.PlatformView.Content = null;
return;
}
// Create handler for content
if (content.Handler == null)
{
content.Handler = content.ToViewHandler(handler.MauiContext);
}
if (content.Handler?.PlatformView is SkiaView skiaContent)
{
handler.PlatformView.Content = skiaContent;
}
}
public static void MapLeftItems(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.LeftItems.Clear();
if (swipeView.LeftItems != null)
{
foreach (var item in swipeView.LeftItems)
{
handler.PlatformView.LeftItems.Add(CreatePlatformSwipeItem(item));
}
}
}
public static void MapRightItems(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.RightItems.Clear();
if (swipeView.RightItems != null)
{
foreach (var item in swipeView.RightItems)
{
handler.PlatformView.RightItems.Add(CreatePlatformSwipeItem(item));
}
}
}
public static void MapTopItems(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.TopItems.Clear();
if (swipeView.TopItems != null)
{
foreach (var item in swipeView.TopItems)
{
handler.PlatformView.TopItems.Add(CreatePlatformSwipeItem(item));
}
}
}
public static void MapBottomItems(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.BottomItems.Clear();
if (swipeView.BottomItems != null)
{
foreach (var item in swipeView.BottomItems)
{
handler.PlatformView.BottomItems.Add(CreatePlatformSwipeItem(item));
}
}
}
public static void MapThreshold(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
handler.PlatformView.LeftSwipeThreshold = (float)swipeView.Threshold;
handler.PlatformView.RightSwipeThreshold = (float)swipeView.Threshold;
}
public static void MapBackground(SwipeViewHandler handler, SwipeView swipeView)
{
if (handler.PlatformView is null) return;
if (swipeView.Background is SolidColorBrush solidBrush)
{
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
}
}
public static void MapRequestOpen(SwipeViewHandler handler, SwipeView swipeView, object? args)
{
if (handler.PlatformView is null) return;
if (args is SwipeViewOpenRequest request)
{
var direction = request.OpenSwipeItem switch
{
OpenSwipeItem.LeftItems => Platform.SwipeDirection.Right,
OpenSwipeItem.RightItems => Platform.SwipeDirection.Left,
OpenSwipeItem.TopItems => Platform.SwipeDirection.Down,
OpenSwipeItem.BottomItems => Platform.SwipeDirection.Up,
_ => Platform.SwipeDirection.Right
};
handler.PlatformView.Open(direction);
}
}
public static void MapRequestClose(SwipeViewHandler handler, SwipeView swipeView, object? args)
{
if (handler.PlatformView is null) return;
handler.PlatformView.Close();
}
private static Platform.SwipeItem CreatePlatformSwipeItem(ISwipeItem item)
{
var platformItem = new Platform.SwipeItem();
if (item is Controls.SwipeItem swipeItem)
{
platformItem.Text = swipeItem.Text ?? "";
// Get background color
var bgColor = swipeItem.BackgroundColor;
if (bgColor is not null)
{
platformItem.BackgroundColor = bgColor.ToSKColor();
}
}
else if (item is Controls.SwipeItemView swipeItemView)
{
// SwipeItemView uses custom content - use a simple representation
platformItem.Text = "Action";
platformItem.BackgroundColor = new SKColor(100, 100, 100);
}
return platformItem;
}
}