2025-12-19 09:30:16 +00:00
|
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
|
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using Microsoft.Maui.Controls;
|
2025-12-19 09:30:16 +00:00
|
|
|
using SkiaSharp;
|
|
|
|
|
|
|
|
|
|
namespace Microsoft.Maui.Platform;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Selection mode for collection views.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum SkiaSelectionMode
|
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
Single,
|
|
|
|
|
Multiple
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Layout orientation for items.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum ItemsLayoutOrientation
|
|
|
|
|
{
|
|
|
|
|
Vertical,
|
|
|
|
|
Horizontal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Skia-rendered CollectionView with selection, headers, and flexible layouts.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class SkiaCollectionView : SkiaItemsView
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
#region BindableProperties
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
public static readonly BindableProperty SelectionModeProperty = BindableProperty.Create(
|
|
|
|
|
nameof(SelectionMode),
|
|
|
|
|
typeof(SkiaSelectionMode),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
SkiaSelectionMode.Single,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectionModeChanged());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(
|
|
|
|
|
nameof(SelectedItem),
|
|
|
|
|
typeof(object),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
null,
|
|
|
|
|
BindingMode.OneWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectedItemChanged(n));
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty OrientationProperty = BindableProperty.Create(
|
|
|
|
|
nameof(Orientation),
|
|
|
|
|
typeof(ItemsLayoutOrientation),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
ItemsLayoutOrientation.Vertical,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty SpanCountProperty = BindableProperty.Create(
|
|
|
|
|
nameof(SpanCount),
|
|
|
|
|
typeof(int),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
1,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate(),
|
|
|
|
|
coerceValue: (b, v) => Math.Max(1, (int)v));
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty GridItemWidthProperty = BindableProperty.Create(
|
|
|
|
|
nameof(GridItemWidth),
|
|
|
|
|
typeof(float),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
100f,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty HeaderProperty = BindableProperty.Create(
|
|
|
|
|
nameof(Header),
|
|
|
|
|
typeof(object),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
null,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnHeaderChanged(n));
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty FooterProperty = BindableProperty.Create(
|
|
|
|
|
nameof(Footer),
|
|
|
|
|
typeof(object),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
null,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnFooterChanged(n));
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty HeaderHeightProperty = BindableProperty.Create(
|
|
|
|
|
nameof(HeaderHeight),
|
|
|
|
|
typeof(float),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
0f,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty FooterHeightProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FooterHeight),
|
|
|
|
|
typeof(float),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
0f,
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty SelectionColorProperty = BindableProperty.Create(
|
|
|
|
|
nameof(SelectionColor),
|
|
|
|
|
typeof(SKColor),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
new SKColor(33, 150, 243, 89),
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty HeaderBackgroundColorProperty = BindableProperty.Create(
|
|
|
|
|
nameof(HeaderBackgroundColor),
|
|
|
|
|
typeof(SKColor),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
new SKColor(245, 245, 245),
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
public static readonly BindableProperty FooterBackgroundColorProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FooterBackgroundColor),
|
|
|
|
|
typeof(SKColor),
|
|
|
|
|
typeof(SkiaCollectionView),
|
|
|
|
|
new SKColor(245, 245, 245),
|
|
|
|
|
BindingMode.TwoWay,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
private List<object> _selectedItems = new List<object>();
|
2025-12-19 09:30:16 +00:00
|
|
|
private int _selectedIndex = -1;
|
2026-01-01 13:51:12 -05:00
|
|
|
private bool _isSelectingItem;
|
2025-12-21 13:26:56 -05:00
|
|
|
private bool _heightsChangedDuringDraw;
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
public SkiaSelectionMode SelectionMode
|
|
|
|
|
{
|
|
|
|
|
get => (SkiaSelectionMode)GetValue(SelectionModeProperty);
|
|
|
|
|
set => SetValue(SelectionModeProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? SelectedItem
|
|
|
|
|
{
|
|
|
|
|
get => GetValue(SelectedItemProperty);
|
|
|
|
|
set => SetValue(SelectedItemProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
public IList<object> SelectedItems => _selectedItems.AsReadOnly();
|
|
|
|
|
|
|
|
|
|
public override int SelectedIndex
|
|
|
|
|
{
|
|
|
|
|
get => _selectedIndex;
|
|
|
|
|
set
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
if (SelectionMode != SkiaSelectionMode.None)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
var item = GetItemAt(value);
|
|
|
|
|
if (item != null)
|
|
|
|
|
{
|
|
|
|
|
SelectedItem = item;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ItemsLayoutOrientation Orientation
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => (ItemsLayoutOrientation)GetValue(OrientationProperty);
|
|
|
|
|
set => SetValue(OrientationProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int SpanCount
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => (int)GetValue(SpanCountProperty);
|
|
|
|
|
set => SetValue(SpanCountProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float GridItemWidth
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => (float)GetValue(GridItemWidthProperty);
|
|
|
|
|
set => SetValue(GridItemWidthProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? Header
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => GetValue(HeaderProperty);
|
|
|
|
|
set => SetValue(HeaderProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object? Footer
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => GetValue(FooterProperty);
|
|
|
|
|
set => SetValue(FooterProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float HeaderHeight
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => (float)GetValue(HeaderHeightProperty);
|
|
|
|
|
set => SetValue(HeaderHeightProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float FooterHeight
|
|
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
get => (float)GetValue(FooterHeightProperty);
|
|
|
|
|
set => SetValue(FooterHeightProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
public SKColor SelectionColor
|
|
|
|
|
{
|
|
|
|
|
get => (SKColor)GetValue(SelectionColorProperty);
|
|
|
|
|
set => SetValue(SelectionColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SKColor HeaderBackgroundColor
|
|
|
|
|
{
|
|
|
|
|
get => (SKColor)GetValue(HeaderBackgroundColorProperty);
|
|
|
|
|
set => SetValue(HeaderBackgroundColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SKColor FooterBackgroundColor
|
|
|
|
|
{
|
|
|
|
|
get => (SKColor)GetValue(FooterBackgroundColorProperty);
|
|
|
|
|
set => SetValue(FooterBackgroundColorProperty, value);
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
public event EventHandler<CollectionSelectionChangedEventArgs>? SelectionChanged;
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
protected override void RefreshItems()
|
|
|
|
|
{
|
|
|
|
|
_selectedItems.Clear();
|
|
|
|
|
SetValue(SelectedItemProperty, null);
|
|
|
|
|
_selectedIndex = -1;
|
|
|
|
|
base.RefreshItems();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnSelectionModeChanged()
|
|
|
|
|
{
|
|
|
|
|
switch (SelectionMode)
|
|
|
|
|
{
|
|
|
|
|
case SkiaSelectionMode.None:
|
|
|
|
|
ClearSelection();
|
|
|
|
|
break;
|
|
|
|
|
case SkiaSelectionMode.Single:
|
|
|
|
|
if (_selectedItems.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
var first = _selectedItems.FirstOrDefault();
|
|
|
|
|
ClearSelection();
|
|
|
|
|
if (first != null)
|
|
|
|
|
{
|
|
|
|
|
SelectItem(first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnSelectedItemChanged(object? newValue)
|
|
|
|
|
{
|
|
|
|
|
if (SelectionMode != SkiaSelectionMode.None && !_isSelectingItem)
|
|
|
|
|
{
|
|
|
|
|
ClearSelection();
|
|
|
|
|
if (newValue != null)
|
|
|
|
|
{
|
|
|
|
|
SelectItem(newValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnHeaderChanged(object? newValue)
|
|
|
|
|
{
|
|
|
|
|
HeaderHeight = newValue != null ? 44 : 0;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnFooterChanged(object? newValue)
|
|
|
|
|
{
|
|
|
|
|
FooterHeight = newValue != null ? 44 : 0;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
private void SelectItem(object item)
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
if (SelectionMode == SkiaSelectionMode.None)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var previousSelection = _selectedItems.ToList();
|
2025-12-19 09:30:16 +00:00
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (SelectionMode == SkiaSelectionMode.Single)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
|
|
|
|
_selectedItems.Clear();
|
|
|
|
|
_selectedItems.Add(item);
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
SetValue(SelectedItemProperty, item);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
for (int i = 0; i < ItemCount; i++)
|
|
|
|
|
{
|
|
|
|
|
if (GetItemAt(i) == item)
|
|
|
|
|
{
|
|
|
|
|
_selectedIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-01 13:51:12 -05:00
|
|
|
else
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
|
|
|
|
if (_selectedItems.Contains(item))
|
|
|
|
|
{
|
|
|
|
|
_selectedItems.Remove(item);
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (SelectedItem == item)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
SetValue(SelectedItemProperty, _selectedItems.FirstOrDefault());
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_selectedItems.Add(item);
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
SetValue(SelectedItemProperty, item);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
_selectedIndex = SelectedItem != null ? GetIndexOf(SelectedItem) : -1;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
SelectionChanged?.Invoke(this, new CollectionSelectionChangedEventArgs(previousSelection, _selectedItems.ToList()));
|
2025-12-19 09:30:16 +00:00
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int GetIndexOf(object item)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < ItemCount; i++)
|
|
|
|
|
{
|
|
|
|
|
if (GetItemAt(i) == item)
|
2026-01-01 13:51:12 -05:00
|
|
|
{
|
2025-12-19 09:30:16 +00:00
|
|
|
return i;
|
2026-01-01 13:51:12 -05:00
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ClearSelection()
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
var previousItems = _selectedItems.ToList();
|
2025-12-19 09:30:16 +00:00
|
|
|
_selectedItems.Clear();
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
SetValue(SelectedItemProperty, null);
|
2025-12-19 09:30:16 +00:00
|
|
|
_selectedIndex = -1;
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
if (previousItems.Count > 0)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
SelectionChanged?.Invoke(this, new CollectionSelectionChangedEventArgs(previousItems, new List<object>()));
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnItemTapped(int index, object item)
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
if (_isSelectingItem)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
return;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
_isSelectingItem = true;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (SelectionMode != SkiaSelectionMode.None)
|
|
|
|
|
{
|
|
|
|
|
SelectItem(item);
|
|
|
|
|
}
|
|
|
|
|
base.OnItemTapped(index, item);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_isSelectingItem = false;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void DrawItem(SKCanvas canvas, object item, int index, SKRect bounds, SKPaint paint)
|
|
|
|
|
{
|
|
|
|
|
bool isSelected = _selectedItems.Contains(item);
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (Orientation == ItemsLayoutOrientation.Vertical && SpanCount == 1)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
paint.Color = new SKColor(224, 224, 224);
|
2025-12-19 09:30:16 +00:00
|
|
|
paint.Style = SKPaintStyle.Stroke;
|
2026-01-01 13:51:12 -05:00
|
|
|
paint.StrokeWidth = 1f;
|
2025-12-19 09:30:16 +00:00
|
|
|
canvas.DrawLine(bounds.Left, bounds.Bottom, bounds.Right, bounds.Bottom, paint);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
if (ItemViewCreator != null)
|
|
|
|
|
{
|
|
|
|
|
if (!_itemViewCache.TryGetValue(index, out var itemView) || itemView == null)
|
|
|
|
|
{
|
|
|
|
|
itemView = ItemViewCreator(item);
|
|
|
|
|
if (itemView != null)
|
|
|
|
|
{
|
|
|
|
|
itemView.Parent = this;
|
|
|
|
|
_itemViewCache[index] = itemView;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (itemView != null)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var availableSize = new SKSize(bounds.Width, float.MaxValue);
|
|
|
|
|
var measuredSize = itemView.Measure(availableSize);
|
|
|
|
|
|
|
|
|
|
var rawHeight = measuredSize.Height;
|
2026-01-01 13:51:12 -05:00
|
|
|
if (float.IsNaN(rawHeight) || float.IsInfinity(rawHeight) || rawHeight > 10000f)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
|
|
|
|
rawHeight = ItemHeight;
|
|
|
|
|
}
|
2026-01-01 13:51:12 -05:00
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
var measuredHeight = Math.Max(rawHeight, ItemHeight);
|
2026-01-01 13:51:12 -05:00
|
|
|
if (!_itemHeights.TryGetValue(index, out var cachedHeight) || Math.Abs(cachedHeight - measuredHeight) > 1f)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
|
|
|
|
_itemHeights[index] = measuredHeight;
|
2026-01-01 13:51:12 -05:00
|
|
|
_heightsChangedDuringDraw = true;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var actualBounds = new SKRect(bounds.Left, bounds.Top, bounds.Right, bounds.Top + measuredHeight);
|
|
|
|
|
itemView.Arrange(actualBounds);
|
|
|
|
|
itemView.Draw(canvas);
|
|
|
|
|
|
|
|
|
|
if (isSelected)
|
|
|
|
|
{
|
|
|
|
|
paint.Color = SelectionColor;
|
|
|
|
|
paint.Style = SKPaintStyle.Fill;
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.DrawRoundRect(actualBounds, 12f, 12f, paint);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (isSelected && SelectionMode == SkiaSelectionMode.Multiple)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
DrawCheckmark(canvas, new SKRect(actualBounds.Right - 32f, actualBounds.MidY - 8f, actualBounds.Right - 16f, actualBounds.MidY + 8f));
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-01 13:51:12 -05:00
|
|
|
return;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Console.WriteLine("[SkiaCollectionView.DrawItem] EXCEPTION: " + ex.Message + "\n" + ex.StackTrace);
|
|
|
|
|
return;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
if (ItemRenderer != null && ItemRenderer(item, index, bounds, canvas, paint))
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
return;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
paint.Color = SKColors.Black;
|
|
|
|
|
paint.Style = SKPaintStyle.Fill;
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
using var font = new SKFont(SKTypeface.Default, 14f, 1f, 0f);
|
2025-12-19 09:30:16 +00:00
|
|
|
using var textPaint = new SKPaint(font)
|
|
|
|
|
{
|
|
|
|
|
Color = SKColors.Black,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var text = item?.ToString() ?? "";
|
|
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
textPaint.MeasureText(text, ref textBounds);
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var x = bounds.Left + 16f;
|
2025-12-19 09:30:16 +00:00
|
|
|
var y = bounds.MidY - textBounds.MidY;
|
|
|
|
|
canvas.DrawText(text, x, y, textPaint);
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (isSelected && SelectionMode == SkiaSelectionMode.Multiple)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
DrawCheckmark(canvas, new SKRect(bounds.Right - 32f, bounds.MidY - 8f, bounds.Right - 16f, bounds.MidY + 8f));
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawCheckmark(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
using var paint = new SKPaint
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Color = new SKColor(33, 150, 243),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Stroke,
|
2026-01-01 13:51:12 -05:00
|
|
|
StrokeWidth = 2f,
|
2025-12-19 09:30:16 +00:00
|
|
|
IsAntialias = true,
|
|
|
|
|
StrokeCap = SKStrokeCap.Round
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using var path = new SKPath();
|
|
|
|
|
path.MoveTo(bounds.Left, bounds.MidY);
|
2026-01-01 13:51:12 -05:00
|
|
|
path.LineTo(bounds.MidX - 2f, bounds.Bottom - 2f);
|
|
|
|
|
path.LineTo(bounds.Right, bounds.Top + 2f);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
canvas.DrawPath(path, paint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
_heightsChangedDuringDraw = false;
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
if (BackgroundColor != SKColors.Transparent)
|
|
|
|
|
{
|
|
|
|
|
using var bgPaint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Color = BackgroundColor,
|
|
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
|
|
|
|
canvas.DrawRect(bounds, bgPaint);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
if (Header != null && HeaderHeight > 0f)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
var headerRect = new SKRect(bounds.Left, bounds.Top, bounds.Right, bounds.Top + HeaderHeight);
|
2025-12-19 09:30:16 +00:00
|
|
|
DrawHeader(canvas, headerRect);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
if (Footer != null && FooterHeight > 0f)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
var footerRect = new SKRect(bounds.Left, bounds.Bottom - FooterHeight, bounds.Right, bounds.Bottom);
|
2025-12-19 09:30:16 +00:00
|
|
|
DrawFooter(canvas, footerRect);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var contentBounds = new SKRect(bounds.Left, bounds.Top + HeaderHeight, bounds.Right, bounds.Bottom - FooterHeight);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
if (ItemCount == 0)
|
|
|
|
|
{
|
|
|
|
|
DrawEmptyView(canvas, contentBounds);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
if (SpanCount > 1)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
|
|
|
|
DrawGridItems(canvas, contentBounds);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DrawListItems(canvas, contentBounds);
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
if (_heightsChangedDuringDraw)
|
|
|
|
|
{
|
|
|
|
|
_heightsChangedDuringDraw = false;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawListItems(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
canvas.Save();
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.ClipRect(bounds, SKClipOperation.Intersect, false);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
using var paint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
var scrollOffset = GetScrollOffset();
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
int firstVisible = 0;
|
2026-01-01 13:51:12 -05:00
|
|
|
float cumulativeOffset = 0f;
|
2025-12-21 13:26:56 -05:00
|
|
|
for (int i = 0; i < ItemCount; i++)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
var itemH = GetItemHeight(i);
|
|
|
|
|
if (cumulativeOffset + itemH > scrollOffset)
|
|
|
|
|
{
|
|
|
|
|
firstVisible = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
cumulativeOffset += itemH + ItemSpacing;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
float currentY = bounds.Top + GetItemOffset(firstVisible) - scrollOffset;
|
|
|
|
|
for (int i = firstVisible; i < ItemCount; i++)
|
|
|
|
|
{
|
|
|
|
|
var itemH = GetItemHeight(i);
|
2026-01-01 13:51:12 -05:00
|
|
|
var itemRect = new SKRect(bounds.Left, currentY, bounds.Right - 8f, currentY + itemH);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
if (itemRect.Top > bounds.Bottom)
|
2026-01-01 13:51:12 -05:00
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
break;
|
2026-01-01 13:51:12 -05:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
if (itemRect.Bottom >= bounds.Top)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
var item = GetItemAt(i);
|
|
|
|
|
if (item != null)
|
|
|
|
|
{
|
|
|
|
|
DrawItem(canvas, item, i, itemRect, paint);
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
currentY += itemH + ItemSpacing;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canvas.Restore();
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
var totalHeight = TotalContentHeight;
|
2025-12-19 09:30:16 +00:00
|
|
|
if (totalHeight > bounds.Height)
|
|
|
|
|
{
|
|
|
|
|
DrawScrollBarInternal(canvas, bounds, scrollOffset, totalHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawGridItems(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
canvas.Save();
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.ClipRect(bounds, SKClipOperation.Intersect, false);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
using var paint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var cellWidth = (bounds.Width - 8f) / SpanCount;
|
2025-12-19 09:30:16 +00:00
|
|
|
var cellHeight = ItemHeight;
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
var rowCount = (int)Math.Ceiling((double)ItemCount / SpanCount);
|
2025-12-19 09:30:16 +00:00
|
|
|
var totalHeight = rowCount * (cellHeight + ItemSpacing) - ItemSpacing;
|
|
|
|
|
|
|
|
|
|
var scrollOffset = GetScrollOffset();
|
|
|
|
|
var firstVisibleRow = Math.Max(0, (int)(scrollOffset / (cellHeight + ItemSpacing)));
|
2026-01-01 13:51:12 -05:00
|
|
|
var lastVisibleRow = Math.Min(rowCount - 1, (int)((scrollOffset + bounds.Height) / (cellHeight + ItemSpacing)) + 1);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
for (int row = firstVisibleRow; row <= lastVisibleRow; row++)
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
var rowY = bounds.Top + row * (cellHeight + ItemSpacing) - scrollOffset;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
for (int col = 0; col < SpanCount; col++)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
RC1: Full XAML support with BindableProperty, VSM, and data binding
Phase 1 - BindableProperty Foundation:
- SkiaLayoutView: Convert Spacing, Padding, ClipToBounds to BindableProperty
- SkiaStackLayout: Convert Orientation to BindableProperty
- SkiaGrid: Convert RowSpacing, ColumnSpacing to BindableProperty
- SkiaCollectionView: Convert all 12 properties to BindableProperty
- SkiaShell: Convert all 12 properties to BindableProperty
Phase 2 - Visual State Manager:
- Add VSM integration to SkiaImageButton pointer handlers
- Support Normal, PointerOver, Pressed, Disabled states
Phase 3-4 - XAML/Data Binding:
- Type converters for SKColor, SKRect, SKSize, SKPoint
- BindingContext propagation through visual tree
- Full handler registration for all MAUI controls
Documentation:
- README: Add styling/binding examples, update roadmap
- Add RC1-ROADMAP.md with implementation details
Version: 1.0.0-rc.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:26:04 -05:00
|
|
|
var index = row * SpanCount + col;
|
2026-01-01 13:51:12 -05:00
|
|
|
if (index >= ItemCount)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
var cellX = bounds.Left + col * cellWidth;
|
2026-01-01 13:51:12 -05:00
|
|
|
var cellRect = new SKRect(cellX + 2f, rowY, cellX + cellWidth - 2f, rowY + cellHeight);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
if (cellRect.Bottom < bounds.Top || cellRect.Top > bounds.Bottom)
|
2026-01-01 13:51:12 -05:00
|
|
|
{
|
2025-12-19 09:30:16 +00:00
|
|
|
continue;
|
2026-01-01 13:51:12 -05:00
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
var item = GetItemAt(index);
|
|
|
|
|
if (item != null)
|
|
|
|
|
{
|
|
|
|
|
using var cellBgPaint = new SKPaint
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Color = _selectedItems.Contains(item) ? SelectionColor : new SKColor(250, 250, 250),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.DrawRoundRect(new SKRoundRect(cellRect, 4f), cellBgPaint);
|
2025-12-19 09:30:16 +00:00
|
|
|
DrawItem(canvas, item, index, cellRect, paint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canvas.Restore();
|
|
|
|
|
|
|
|
|
|
if (totalHeight > bounds.Height)
|
|
|
|
|
{
|
|
|
|
|
DrawScrollBarInternal(canvas, bounds, scrollOffset, totalHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawScrollBarInternal(SKCanvas canvas, SKRect bounds, float scrollOffset, float totalHeight)
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
var scrollBarWidth = 6f;
|
|
|
|
|
var scrollBarMargin = 2f;
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
var trackRect = new SKRect(
|
2025-12-21 13:26:56 -05:00
|
|
|
bounds.Right - scrollBarWidth - scrollBarMargin,
|
|
|
|
|
bounds.Top + scrollBarMargin,
|
|
|
|
|
bounds.Right - scrollBarMargin,
|
|
|
|
|
bounds.Bottom - scrollBarMargin);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
using var trackPaint = new SKPaint
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
Color = new SKColor(0, 0, 0, 20),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.DrawRoundRect(new SKRoundRect(trackRect, 3f), trackPaint);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var maxOffset = Math.Max(0f, totalHeight - bounds.Height);
|
2025-12-19 09:30:16 +00:00
|
|
|
var viewportRatio = bounds.Height / totalHeight;
|
2025-12-21 13:26:56 -05:00
|
|
|
var availableTrackHeight = trackRect.Height;
|
2026-01-01 13:51:12 -05:00
|
|
|
var thumbHeight = Math.Max(30f, availableTrackHeight * viewportRatio);
|
|
|
|
|
var scrollRatio = maxOffset > 0f ? scrollOffset / maxOffset : 0f;
|
2025-12-21 13:26:56 -05:00
|
|
|
var thumbY = trackRect.Top + (availableTrackHeight - thumbHeight) * scrollRatio;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var thumbRect = new SKRect(trackRect.Left, thumbY, trackRect.Right, thumbY + thumbHeight);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
using var thumbPaint = new SKPaint
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
Color = new SKColor(100, 100, 100, 180),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Fill,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
canvas.DrawRoundRect(new SKRoundRect(thumbRect, 3f), thumbPaint);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float GetScrollOffset()
|
|
|
|
|
{
|
|
|
|
|
return _scrollOffset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawHeader(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
using var bgPaint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Color = HeaderBackgroundColor,
|
|
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
|
|
|
|
canvas.DrawRect(bounds, bgPaint);
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var text = Header?.ToString() ?? "";
|
2025-12-19 09:30:16 +00:00
|
|
|
if (!string.IsNullOrEmpty(text))
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
using var font = new SKFont(SKTypeface.Default, 16f, 1f, 0f);
|
2025-12-19 09:30:16 +00:00
|
|
|
using var textPaint = new SKPaint(font)
|
|
|
|
|
{
|
|
|
|
|
Color = SKColors.Black,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
textPaint.MeasureText(text, ref textBounds);
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var x = bounds.Left + 16f;
|
2025-12-19 09:30:16 +00:00
|
|
|
var y = bounds.MidY - textBounds.MidY;
|
|
|
|
|
canvas.DrawText(text, x, y, textPaint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using var sepPaint = new SKPaint
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Color = new SKColor(224, 224, 224),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Stroke,
|
2026-01-01 13:51:12 -05:00
|
|
|
StrokeWidth = 1f
|
2025-12-19 09:30:16 +00:00
|
|
|
};
|
|
|
|
|
canvas.DrawLine(bounds.Left, bounds.Bottom, bounds.Right, bounds.Bottom, sepPaint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DrawFooter(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
using var bgPaint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Color = FooterBackgroundColor,
|
|
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
|
|
|
|
canvas.DrawRect(bounds, bgPaint);
|
|
|
|
|
|
|
|
|
|
using var sepPaint = new SKPaint
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Color = new SKColor(224, 224, 224),
|
2025-12-19 09:30:16 +00:00
|
|
|
Style = SKPaintStyle.Stroke,
|
2026-01-01 13:51:12 -05:00
|
|
|
StrokeWidth = 1f
|
2025-12-19 09:30:16 +00:00
|
|
|
};
|
|
|
|
|
canvas.DrawLine(bounds.Left, bounds.Top, bounds.Right, bounds.Top, sepPaint);
|
|
|
|
|
|
2026-01-01 13:51:12 -05:00
|
|
|
var text = Footer?.ToString() ?? "";
|
2025-12-19 09:30:16 +00:00
|
|
|
if (!string.IsNullOrEmpty(text))
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
using var font = new SKFont(SKTypeface.Default, 14f, 1f, 0f);
|
2025-12-19 09:30:16 +00:00
|
|
|
using var textPaint = new SKPaint(font)
|
|
|
|
|
{
|
2026-01-01 13:51:12 -05:00
|
|
|
Color = new SKColor(128, 128, 128),
|
2025-12-19 09:30:16 +00:00
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
textPaint.MeasureText(text, ref textBounds);
|
|
|
|
|
|
|
|
|
|
var x = bounds.MidX - textBounds.MidX;
|
|
|
|
|
var y = bounds.MidY - textBounds.MidY;
|
|
|
|
|
canvas.DrawText(text, x, y, textPaint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Event args for collection selection changed events.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class CollectionSelectionChangedEventArgs : EventArgs
|
|
|
|
|
{
|
|
|
|
|
public IReadOnlyList<object> PreviousSelection { get; }
|
|
|
|
|
public IReadOnlyList<object> CurrentSelection { get; }
|
|
|
|
|
|
|
|
|
|
public CollectionSelectionChangedEventArgs(IList<object> previousSelection, IList<object> currentSelection)
|
|
|
|
|
{
|
|
|
|
|
PreviousSelection = previousSelection.ToList().AsReadOnly();
|
|
|
|
|
CurrentSelection = currentSelection.ToList().AsReadOnly();
|
|
|
|
|
}
|
|
|
|
|
}
|