From 20fcbd78e3fc0378b053c613880ebce9120f9a28 Mon Sep 17 00:00:00 2001 From: logikonline Date: Fri, 16 Jan 2026 05:10:40 +0000 Subject: [PATCH] Picker conmpleted --- Handlers/PickerHandler.cs | 40 ++++- Views/SkiaPicker.cs | 317 +++++++++++++++++++++++++------------- 2 files changed, 241 insertions(+), 116 deletions(-) diff --git a/Handlers/PickerHandler.cs b/Handlers/PickerHandler.cs index e6efa13..5958233 100644 --- a/Handlers/PickerHandler.cs +++ b/Handlers/PickerHandler.cs @@ -5,13 +5,13 @@ using Microsoft.Maui.Handlers; using Microsoft.Maui.Graphics; using Microsoft.Maui.Controls; using Microsoft.Maui.Platform; -using SkiaSharp; using System.Collections.Specialized; namespace Microsoft.Maui.Platform.Linux.Handlers; /// /// Handler for Picker on Linux using Skia rendering. +/// Maps IPicker interface to SkiaPicker platform view. /// public partial class PickerHandler : ViewHandler { @@ -27,6 +27,7 @@ public partial class PickerHandler : ViewHandler [nameof(IPicker.HorizontalTextAlignment)] = MapHorizontalTextAlignment, [nameof(IPicker.VerticalTextAlignment)] = MapVerticalTextAlignment, [nameof(IView.Background)] = MapBackground, + [nameof(IView.IsEnabled)] = MapIsEnabled, [nameof(Picker.ItemsSource)] = MapItemsSource, }; @@ -63,8 +64,17 @@ public partial class PickerHandler : ViewHandler _itemsCollection.CollectionChanged += OnItemsCollectionChanged; } - // Load items + // Load items and sync properties ReloadItems(); + + if (VirtualView != null) + { + MapTitle(this, VirtualView); + MapTitleColor(this, VirtualView); + MapTextColor(this, VirtualView); + MapSelectedIndex(this, VirtualView); + MapIsEnabled(this, VirtualView); + } } protected override void DisconnectHandler(SkiaPicker platformView) @@ -85,11 +95,14 @@ public partial class PickerHandler : ViewHandler ReloadItems(); } - private void OnSelectedIndexChanged(object? sender, EventArgs e) + private void OnSelectedIndexChanged(object? sender, SelectedIndexChangedEventArgs e) { if (VirtualView is null || PlatformView is null) return; - VirtualView.SelectedIndex = PlatformView.SelectedIndex; + if (VirtualView.SelectedIndex != e.NewIndex) + { + VirtualView.SelectedIndex = e.NewIndex; + } } private void ReloadItems() @@ -111,14 +124,18 @@ public partial class PickerHandler : ViewHandler if (handler.PlatformView is null) return; if (picker.TitleColor is not null) { - handler.PlatformView.TitleColor = picker.TitleColor.ToSKColor(); + handler.PlatformView.TitleColor = picker.TitleColor; } } public static void MapSelectedIndex(PickerHandler handler, IPicker picker) { if (handler.PlatformView is null) return; - handler.PlatformView.SelectedIndex = picker.SelectedIndex; + + if (handler.PlatformView.SelectedIndex != picker.SelectedIndex) + { + handler.PlatformView.SelectedIndex = picker.SelectedIndex; + } } public static void MapTextColor(PickerHandler handler, IPicker picker) @@ -126,7 +143,7 @@ public partial class PickerHandler : ViewHandler if (handler.PlatformView is null) return; if (picker.TextColor is not null) { - handler.PlatformView.TextColor = picker.TextColor.ToSKColor(); + handler.PlatformView.TextColor = picker.TextColor; } } @@ -141,7 +158,7 @@ public partial class PickerHandler : ViewHandler } if (font.Size > 0) { - handler.PlatformView.FontSize = (float)font.Size; + handler.PlatformView.FontSize = font.Size; } handler.PlatformView.Invalidate(); } @@ -171,6 +188,13 @@ public partial class PickerHandler : ViewHandler } } + public static void MapIsEnabled(PickerHandler handler, IPicker picker) + { + if (handler.PlatformView is null) return; + handler.PlatformView.IsEnabled = picker.IsEnabled; + handler.PlatformView.Invalidate(); + } + public static void MapItemsSource(PickerHandler handler, IPicker picker) { handler.ReloadItems(); diff --git a/Views/SkiaPicker.cs b/Views/SkiaPicker.cs index 18b0df2..0c2b060 100644 --- a/Views/SkiaPicker.cs +++ b/Views/SkiaPicker.cs @@ -1,32 +1,49 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; using SkiaSharp; namespace Microsoft.Maui.Platform; /// -/// Skia-rendered picker/dropdown control with full XAML styling support. +/// Skia-rendered picker/dropdown control with full MAUI compliance. +/// Implements IPicker interface requirements: +/// - Title, TitleColor for placeholder +/// - SelectedIndex, SelectedItem for selection +/// - TextColor, Font properties for styling +/// - Items collection /// public class SkiaPicker : SkiaView { + #region SKColor Helper + + private static SKColor ToSKColor(Color? color) + { + if (color == null) return SKColors.Transparent; + return new SKColor( + (byte)(color.Red * 255), + (byte)(color.Green * 255), + (byte)(color.Blue * 255), + (byte)(color.Alpha * 255)); + } + + #endregion + #region BindableProperties - /// - /// Bindable property for SelectedIndex. - /// public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create( nameof(SelectedIndex), typeof(int), typeof(SkiaPicker), -1, - BindingMode.OneWay, - propertyChanged: (b, o, n) => ((SkiaPicker)b).OnSelectedIndexChanged()); + BindingMode.TwoWay, + propertyChanged: (b, o, n) => ((SkiaPicker)b).OnSelectedIndexChanged((int)o, (int)n)); - /// - /// Bindable property for Title. - /// public static readonly BindableProperty TitleProperty = BindableProperty.Create( nameof(Title), @@ -36,81 +53,60 @@ public class SkiaPicker : SkiaView BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for TextColor. - /// public static readonly BindableProperty TextColorProperty = BindableProperty.Create( nameof(TextColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - SKColors.Black, + Colors.Black, BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for TitleColor. - /// public static readonly BindableProperty TitleColorProperty = BindableProperty.Create( nameof(TitleColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - new SKColor(0x80, 0x80, 0x80), + Color.FromRgb(0x80, 0x80, 0x80), BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for BorderColor. - /// public static readonly BindableProperty BorderColorProperty = BindableProperty.Create( nameof(BorderColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - new SKColor(0xBD, 0xBD, 0xBD), + Color.FromRgb(0xBD, 0xBD, 0xBD), BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for DropdownBackgroundColor. - /// public static readonly BindableProperty DropdownBackgroundColorProperty = BindableProperty.Create( nameof(DropdownBackgroundColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - SKColors.White, + Colors.White, BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for SelectedItemBackgroundColor. - /// public static readonly BindableProperty SelectedItemBackgroundColorProperty = BindableProperty.Create( nameof(SelectedItemBackgroundColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - new SKColor(0x21, 0x96, 0xF3, 0x30), + Color.FromRgba(0x21, 0x96, 0xF3, 0x30), BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for HoverItemBackgroundColor. - /// public static readonly BindableProperty HoverItemBackgroundColorProperty = BindableProperty.Create( nameof(HoverItemBackgroundColor), - typeof(SKColor), + typeof(Color), typeof(SkiaPicker), - new SKColor(0xE0, 0xE0, 0xE0), + Color.FromRgb(0xE0, 0xE0, 0xE0), BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for FontFamily. - /// public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create( nameof(FontFamily), @@ -120,39 +116,30 @@ public class SkiaPicker : SkiaView BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).InvalidateMeasure()); - /// - /// Bindable property for FontSize. - /// public static readonly BindableProperty FontSizeProperty = BindableProperty.Create( nameof(FontSize), - typeof(float), + typeof(double), typeof(SkiaPicker), - 14f, + 14.0, BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).InvalidateMeasure()); - /// - /// Bindable property for ItemHeight. - /// public static readonly BindableProperty ItemHeightProperty = BindableProperty.Create( nameof(ItemHeight), - typeof(float), + typeof(double), typeof(SkiaPicker), - 40f, + 40.0, BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); - /// - /// Bindable property for CornerRadius. - /// public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create( nameof(CornerRadius), - typeof(float), + typeof(double), typeof(SkiaPicker), - 4f, + 4.0, BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaPicker)b).Invalidate()); @@ -181,54 +168,54 @@ public class SkiaPicker : SkiaView /// /// Gets or sets the text color. /// - public SKColor TextColor + public Color TextColor { - get => (SKColor)GetValue(TextColorProperty); + get => (Color)GetValue(TextColorProperty); set => SetValue(TextColorProperty, value); } /// /// Gets or sets the title color. /// - public SKColor TitleColor + public Color TitleColor { - get => (SKColor)GetValue(TitleColorProperty); + get => (Color)GetValue(TitleColorProperty); set => SetValue(TitleColorProperty, value); } /// /// Gets or sets the border color. /// - public SKColor BorderColor + public Color BorderColor { - get => (SKColor)GetValue(BorderColorProperty); + get => (Color)GetValue(BorderColorProperty); set => SetValue(BorderColorProperty, value); } /// /// Gets or sets the dropdown background color. /// - public SKColor DropdownBackgroundColor + public Color DropdownBackgroundColor { - get => (SKColor)GetValue(DropdownBackgroundColorProperty); + get => (Color)GetValue(DropdownBackgroundColorProperty); set => SetValue(DropdownBackgroundColorProperty, value); } /// /// Gets or sets the selected item background color. /// - public SKColor SelectedItemBackgroundColor + public Color SelectedItemBackgroundColor { - get => (SKColor)GetValue(SelectedItemBackgroundColorProperty); + get => (Color)GetValue(SelectedItemBackgroundColorProperty); set => SetValue(SelectedItemBackgroundColorProperty, value); } /// /// Gets or sets the hover item background color. /// - public SKColor HoverItemBackgroundColor + public Color HoverItemBackgroundColor { - get => (SKColor)GetValue(HoverItemBackgroundColorProperty); + get => (Color)GetValue(HoverItemBackgroundColorProperty); set => SetValue(HoverItemBackgroundColorProperty, value); } @@ -244,27 +231,27 @@ public class SkiaPicker : SkiaView /// /// Gets or sets the font size. /// - public float FontSize + public double FontSize { - get => (float)GetValue(FontSizeProperty); + get => (double)GetValue(FontSizeProperty); set => SetValue(FontSizeProperty, value); } /// /// Gets or sets the item height. /// - public float ItemHeight + public double ItemHeight { - get => (float)GetValue(ItemHeightProperty); + get => (double)GetValue(ItemHeightProperty); set => SetValue(ItemHeightProperty, value); } /// /// Gets or sets the corner radius. /// - public float CornerRadius + public double CornerRadius { - get => (float)GetValue(CornerRadiusProperty); + get => (double)GetValue(CornerRadiusProperty); set => SetValue(CornerRadiusProperty, value); } @@ -304,27 +291,45 @@ public class SkiaPicker : SkiaView #endregion + #region Private Fields + private readonly List _items = new(); private bool _isOpen; - private float _dropdownMaxHeight = 200; + private double _dropdownMaxHeight = 200; private int _hoveredItemIndex = -1; + #endregion + + #region Events + /// /// Event raised when selected index changes. /// - public event EventHandler? SelectedIndexChanged; + public event EventHandler? SelectedIndexChanged; + + #endregion + + #region Constructor public SkiaPicker() { IsFocusable = true; } - private void OnSelectedIndexChanged() + #endregion + + #region Event Handlers + + private void OnSelectedIndexChanged(int oldValue, int newValue) { - SelectedIndexChanged?.Invoke(this, EventArgs.Empty); + SelectedIndexChanged?.Invoke(this, new SelectedIndexChangedEventArgs(oldValue, newValue)); Invalidate(); } + #endregion + + #region Public Methods + /// /// Sets the items in the picker. /// @@ -339,10 +344,13 @@ public class SkiaPicker : SkiaView Invalidate(); } + #endregion + + #region Rendering + private void DrawDropdownOverlay(SKCanvas canvas) { if (_items.Count == 0 || !_isOpen) return; - // Use ScreenBounds for overlay drawing to account for scroll offset DrawDropdown(canvas, ScreenBounds); } @@ -353,21 +361,30 @@ public class SkiaPicker : SkiaView private void DrawPickerButton(SKCanvas canvas, SKRect bounds) { + var cornerRadius = (float)CornerRadius; + var fontSize = (float)FontSize; + + // Get colors + var textColorSK = ToSKColor(TextColor); + var titleColorSK = ToSKColor(TitleColor); + var borderColorSK = ToSKColor(BorderColor); + var focusColorSK = ToSKColor(Color.FromRgb(0x21, 0x96, 0xF3)); + // Draw background using var bgPaint = new SKPaint { - Color = IsEnabled ? BackgroundColor : new SKColor(0xF5, 0xF5, 0xF5), + Color = IsEnabled ? BackgroundColor : ToSKColor(Color.FromRgb(0xF5, 0xF5, 0xF5)), Style = SKPaintStyle.Fill, IsAntialias = true }; - var buttonRect = new SKRoundRect(bounds, CornerRadius); + var buttonRect = new SKRoundRect(bounds, cornerRadius); canvas.DrawRoundRect(buttonRect, bgPaint); // Draw border using var borderPaint = new SKPaint { - Color = IsFocused ? new SKColor(0x21, 0x96, 0xF3) : BorderColor, + Color = IsFocused ? focusColorSK : borderColorSK, Style = SKPaintStyle.Stroke, StrokeWidth = IsFocused ? 2 : 1, IsAntialias = true @@ -375,7 +392,7 @@ public class SkiaPicker : SkiaView canvas.DrawRoundRect(buttonRect, borderPaint); // Draw text or title - using var font = new SKFont(SKTypeface.Default, FontSize); + using var font = new SKFont(SKTypeface.Default, fontSize); using var textPaint = new SKPaint(font) { IsAntialias = true @@ -385,12 +402,12 @@ public class SkiaPicker : SkiaView if (SelectedIndex >= 0 && SelectedIndex < _items.Count) { displayText = _items[SelectedIndex]; - textPaint.Color = IsEnabled ? TextColor : TextColor.WithAlpha(128); + textPaint.Color = IsEnabled ? textColorSK : textColorSK.WithAlpha(128); } else { displayText = Title; - textPaint.Color = TitleColor; + textPaint.Color = titleColorSK; } var textBounds = new SKRect(); @@ -401,14 +418,14 @@ public class SkiaPicker : SkiaView canvas.DrawText(displayText, textX, textY, textPaint); // Draw dropdown arrow - DrawDropdownArrow(canvas, bounds); + DrawDropdownArrow(canvas, bounds, textColorSK); } - private void DrawDropdownArrow(SKCanvas canvas, SKRect bounds) + private void DrawDropdownArrow(SKCanvas canvas, SKRect bounds, SKColor color) { using var paint = new SKPaint { - Color = IsEnabled ? TextColor : TextColor.WithAlpha(128), + Color = IsEnabled ? color : color.WithAlpha(128), Style = SKPaintStyle.Stroke, StrokeWidth = 2, IsAntialias = true, @@ -440,13 +457,25 @@ public class SkiaPicker : SkiaView { if (_items.Count == 0) return; - var dropdownHeight = Math.Min(_items.Count * ItemHeight, _dropdownMaxHeight); + var itemHeight = (float)ItemHeight; + var cornerRadius = (float)CornerRadius; + var fontSize = (float)FontSize; + var dropdownMaxHeight = (float)_dropdownMaxHeight; + + var dropdownHeight = Math.Min(_items.Count * itemHeight, dropdownMaxHeight); var dropdownRect = new SKRect( bounds.Left, bounds.Bottom + 4, bounds.Right, bounds.Bottom + 4 + dropdownHeight); + // Get colors + var dropdownBgColorSK = ToSKColor(DropdownBackgroundColor); + var borderColorSK = ToSKColor(BorderColor); + var textColorSK = ToSKColor(TextColor); + var selectedBgColorSK = ToSKColor(SelectedItemBackgroundColor); + var hoverBgColorSK = ToSKColor(HoverItemBackgroundColor); + // Draw shadow using var shadowPaint = new SKPaint { @@ -455,52 +484,52 @@ public class SkiaPicker : SkiaView Style = SKPaintStyle.Fill }; var shadowRect = new SKRect(dropdownRect.Left + 2, dropdownRect.Top + 2, dropdownRect.Right + 2, dropdownRect.Bottom + 2); - canvas.DrawRoundRect(new SKRoundRect(shadowRect, CornerRadius), shadowPaint); + canvas.DrawRoundRect(new SKRoundRect(shadowRect, cornerRadius), shadowPaint); // Draw dropdown background using var bgPaint = new SKPaint { - Color = DropdownBackgroundColor, + Color = dropdownBgColorSK, Style = SKPaintStyle.Fill, IsAntialias = true }; - canvas.DrawRoundRect(new SKRoundRect(dropdownRect, CornerRadius), bgPaint); + canvas.DrawRoundRect(new SKRoundRect(dropdownRect, cornerRadius), bgPaint); // Draw border using var borderPaint = new SKPaint { - Color = BorderColor, + Color = borderColorSK, Style = SKPaintStyle.Stroke, StrokeWidth = 1, IsAntialias = true }; - canvas.DrawRoundRect(new SKRoundRect(dropdownRect, CornerRadius), borderPaint); + canvas.DrawRoundRect(new SKRoundRect(dropdownRect, cornerRadius), borderPaint); // Clip to dropdown bounds canvas.Save(); - canvas.ClipRoundRect(new SKRoundRect(dropdownRect, CornerRadius)); + canvas.ClipRoundRect(new SKRoundRect(dropdownRect, cornerRadius)); // Draw items - using var font = new SKFont(SKTypeface.Default, FontSize); + using var font = new SKFont(SKTypeface.Default, fontSize); using var textPaint = new SKPaint(font) { - Color = TextColor, + Color = textColorSK, IsAntialias = true }; for (int i = 0; i < _items.Count; i++) { - var itemTop = dropdownRect.Top + i * ItemHeight; + var itemTop = dropdownRect.Top + i * itemHeight; if (itemTop > dropdownRect.Bottom) break; - var itemRect = new SKRect(dropdownRect.Left, itemTop, dropdownRect.Right, itemTop + ItemHeight); + var itemRect = new SKRect(dropdownRect.Left, itemTop, dropdownRect.Right, itemTop + itemHeight); // Draw item background if (i == SelectedIndex) { using var selectedPaint = new SKPaint { - Color = SelectedItemBackgroundColor, + Color = selectedBgColorSK, Style = SKPaintStyle.Fill }; canvas.DrawRect(itemRect, selectedPaint); @@ -509,7 +538,7 @@ public class SkiaPicker : SkiaView { using var hoverPaint = new SKPaint { - Color = HoverItemBackgroundColor, + Color = hoverBgColorSK, Style = SKPaintStyle.Fill }; canvas.DrawRect(itemRect, hoverPaint); @@ -527,18 +556,23 @@ public class SkiaPicker : SkiaView canvas.Restore(); } + #endregion + + #region Pointer Events + public override void OnPointerPressed(PointerEventArgs e) { if (!IsEnabled) return; + var itemHeight = (float)ItemHeight; + if (IsOpen) { - // Use ScreenBounds for popup coordinate calculations (accounts for scroll offset) var screenBounds = ScreenBounds; var dropdownTop = screenBounds.Bottom + 4; if (e.Y >= dropdownTop) { - var itemIndex = (int)((e.Y - dropdownTop) / ItemHeight); + var itemIndex = (int)((e.Y - dropdownTop) / itemHeight); if (itemIndex >= 0 && itemIndex < _items.Count) { SelectedIndex = itemIndex; @@ -551,6 +585,7 @@ public class SkiaPicker : SkiaView IsOpen = true; } + e.Handled = true; Invalidate(); } @@ -558,12 +593,13 @@ public class SkiaPicker : SkiaView { if (!_isOpen) return; - // Use ScreenBounds for popup coordinate calculations (accounts for scroll offset) + var itemHeight = (float)ItemHeight; var screenBounds = ScreenBounds; var dropdownTop = screenBounds.Bottom + 4; + if (e.Y >= dropdownTop) { - var newHovered = (int)((e.Y - dropdownTop) / ItemHeight); + var newHovered = (int)((e.Y - dropdownTop) / itemHeight); if (newHovered != _hoveredItemIndex && newHovered >= 0 && newHovered < _items.Count) { _hoveredItemIndex = newHovered; @@ -586,6 +622,10 @@ public class SkiaPicker : SkiaView Invalidate(); } + #endregion + + #region Keyboard Events + public override void OnKeyDown(KeyEventArgs e) { if (!IsEnabled) return; @@ -623,9 +663,29 @@ public class SkiaPicker : SkiaView e.Handled = true; } break; + + case Key.Home: + if (_items.Count > 0) + { + SelectedIndex = 0; + e.Handled = true; + } + break; + + case Key.End: + if (_items.Count > 0) + { + SelectedIndex = _items.Count - 1; + e.Handled = true; + } + break; } } + #endregion + + #region Lifecycle + public override void OnFocusLost() { base.OnFocusLost(); @@ -635,6 +695,18 @@ public class SkiaPicker : SkiaView } } + protected override void OnEnabledChanged() + { + base.OnEnabledChanged(); + SkiaVisualStateManager.GoToState(this, IsEnabled + ? SkiaVisualStateManager.CommonStates.Normal + : SkiaVisualStateManager.CommonStates.Disabled); + } + + #endregion + + #region Layout + protected override SKSize MeasureOverride(SKSize availableSize) { return new SKSize( @@ -642,12 +714,15 @@ public class SkiaPicker : SkiaView 40); } + #endregion + + #region Hit Testing + /// /// Override to include dropdown area in hit testing. /// protected override bool HitTestPopupArea(float x, float y) { - // Use ScreenBounds for hit testing (accounts for scroll offset) var screenBounds = ScreenBounds; // Always include the picker button itself @@ -657,7 +732,9 @@ public class SkiaPicker : SkiaView // When open, also include the dropdown area if (_isOpen && _items.Count > 0) { - var dropdownHeight = Math.Min(_items.Count * ItemHeight, _dropdownMaxHeight); + var itemHeight = (float)ItemHeight; + var dropdownMaxHeight = (float)_dropdownMaxHeight; + var dropdownHeight = Math.Min(_items.Count * itemHeight, dropdownMaxHeight); var dropdownRect = new SKRect( screenBounds.Left, screenBounds.Bottom + 4, @@ -669,4 +746,28 @@ public class SkiaPicker : SkiaView return false; } + + #endregion +} + +/// +/// Event args for selected index changed events. +/// +public class SelectedIndexChangedEventArgs : EventArgs +{ + /// + /// Gets the old selected index. + /// + public int OldIndex { get; } + + /// + /// Gets the new selected index. + /// + public int NewIndex { get; } + + public SelectedIndexChangedEventArgs(int oldIndex, int newIndex) + { + OldIndex = oldIndex; + NewIndex = newIndex; + } }