SegmentedControl.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using Plugin.Segmented.Event;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
namespace Plugin.Segmented.Control
{
[DesignTimeVisible(true)]
[Preserve(AllMembers = true)]
public clast SegmentedControl : View, IViewContainer
{
public SegmentedControl()
{
Children = new List();
}
public event EventHandler OnElementChildrenChanging;
public event EventHandler OnSegmentSelected;
#region Children
public static readonly BindableProperty ChildrenProperty = BindableProperty.Create(nameof(Children), typeof(IList), typeof(SegmentedControl), default(IList), propertyChanging: OnChildrenChanging);
private static void OnChildrenChanging(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is SegmentedControl segmentedControl
&& newValue is IList newItemsList
&& segmentedControl.Children != null)
{
segmentedControl.OnElementChildrenChanging?.Invoke(segmentedControl, new ElementChildrenChanging((IList)oldValue, newItemsList));
segmentedControl.Children.Clear();
foreach (var newSegment in newItemsList)
{
newSegment.BindingContext = segmentedControl.BindingContext;
segmentedControl.Children.Add(newSegment);
}
}
}
public IList Children
{
get => (IList)GetValue(ChildrenProperty);
set => SetValue(ChildrenProperty, value);
}
#endregion
#region ItemsSource
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(SegmentedControl));
public static readonly BindableProperty TextPropertyNameProperty = BindableProperty.Create(nameof(TextPropertyName), typeof(string), typeof(SegmentedControl));
private void OnItemsSourceChanged()
{
var itemsSource = ItemsSource;
var items = itemsSource as IList;
if (items == null && itemsSource is IEnumerable list)
items = list.Cast().ToList();
if (items != null)
{
var textValues = items as IEnumerable;
if (textValues == null && items.Count > 0 && items[0] is string)
textValues = items.Cast();
if (textValues != null)
{
Children = new List(textValues.Select(child => new SegmentedControlOption {Text = child}));
OnSelectedItemChanged(true);
}
else
{
var textPropertyName = TextPropertyName;
if (textPropertyName != null)
{
var newChildren = new List();
foreach (var item in items)
newChildren.Add(new SegmentedControlOption { Item = item, TextPropertyName = textPropertyName });
Children = newChildren;
OnSelectedItemChanged(true);
}
}
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(ItemsSource) || propertyName == nameof(TextPropertyName))
OnItemsSourceChanged();
else if(propertyName == nameof(SelectedItem))
OnSelectedItemChanged();
else if(propertyName == nameof(SelectedSegment))
OnSelectedSegmentChanged();
}
private void OnSelectedSegmentChanged()
{
var segmentIndex = SelectedSegment;
if (segmentIndex >= 0 && segmentIndex < Children.Count && SelectedItem != Children[segmentIndex].Item)
SelectedItem = Children[segmentIndex].Item;
}
private void OnSelectedItemChanged(bool forceUpdateSelectedSegment = false)
{
if (TextPropertyName != null)
{
var selectedItem = SelectedItem;
var selectedIndex = Children.IndexOf(item => item.Item == selectedItem);
if (selectedIndex == -1)
{
selectedIndex = SelectedSegment;
if (selectedIndex < 0 || selectedIndex >= Children.Count)
SelectedSegment = 0;
else if(SelectedSegment != selectedIndex)
SelectedSegment = selectedIndex;
else if(forceUpdateSelectedSegment)
OnSelectedSegmentChanged();
}
else if (selectedIndex != SelectedSegment)
SelectedSegment = selectedIndex;
}
}
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public string TextPropertyName
{
get => (string)GetValue(TextPropertyNameProperty);
set => SetValue(TextPropertyNameProperty, value);
}
#endregion
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
propertyName: "TextColor",
returnType: typeof(Color),
declaringType: typeof(SegmentedControl),
defaultValue: default(Color));
public Color TextColor
{
get => (Color) GetValue(TextColorProperty);
set => SetValue(TextColorProperty, value);
}
public static readonly BindableProperty TintColorProperty = BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(SegmentedControl), Color.Blue);
public Color TintColor
{
get => (Color)GetValue(TintColorProperty);
set => SetValue(TintColorProperty, value);
}
public static readonly BindableProperty SelectedTextColorProperty = BindableProperty.Create(nameof(SelectedTextColor), typeof(Color), typeof(SegmentedControl), Color.White);
public Color SelectedTextColor
{
get => (Color)GetValue(SelectedTextColorProperty);
set => SetValue(SelectedTextColorProperty, value);
}
public static readonly BindableProperty DisabledColorProperty = BindableProperty.Create(nameof(DisabledColor), typeof(Color), typeof(SegmentedControl), Color.Gray);
public Color DisabledColor
{
get => (Color)GetValue(DisabledColorProperty);
set => SetValue(DisabledColorProperty, value);
}
public static readonly BindableProperty BorderColorProperty = BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(SegmentedControl), defaultValueCreator: bindable => ((SegmentedControl)bindable).TintColor);
public Color BorderColor
{
get => (Color)GetValue(BorderColorProperty);
set => SetValue(BorderColorProperty, value);
}
public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create(nameof(BorderWidth), typeof(double), typeof(SegmentedControl), defaultValueCreator: _ => Device.RuntimePlatform == Device.Android ? 1.0 : 0.0);
public double BorderWidth
{
get => (double)GetValue(BorderWidthProperty);
set => SetValue(BorderWidthProperty, value);
}
public static readonly BindableProperty SelectedSegmentProperty = BindableProperty.Create(nameof(SelectedSegment), typeof(int), typeof(SegmentedControl), 0);
public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(SegmentedControl), defaultBindingMode: BindingMode.TwoWay);
public int SelectedSegment
{
get => (int)GetValue(SelectedSegmentProperty);
set => SetValue(SelectedSegmentProperty, value);
}
public object SelectedItem
{
get => (object)GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public static readonly BindableProperty SegmentSelectedCommandProperty = BindableProperty.Create(nameof(SegmentSelectedCommand), typeof(ICommand), typeof(SegmentedControl));
public ICommand SegmentSelectedCommand
{
get => (ICommand)GetValue(SegmentSelectedCommandProperty);
set => SetValue(SegmentSelectedCommandProperty, value);
}
public static readonly BindableProperty SegmentSelectedCommandParameterProperty = BindableProperty.Create(nameof(SegmentSelectedCommandParameter), typeof(object), typeof(SegmentedControl));
public object SegmentSelectedCommandParameter
{
get => GetValue(SegmentSelectedCommandParameterProperty);
set => SetValue(SegmentSelectedCommandParameterProperty, value);
}
public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(nameof(FontSize), typeof(double), typeof(SegmentedControl), Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
[Xamarin.Forms.TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(SegmentedControl));
public string FontFamily
{
get => (string)GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public void RaiseSelectionChanged()
{
OnSegmentSelected?.Invoke(this, new SegmentSelectEventArgs { NewValue = this.SelectedSegment });
if (!(SegmentSelectedCommand is null) && SegmentSelectedCommand.CanExecute(SegmentSelectedCommandParameter))
{
SegmentSelectedCommand.Execute(SegmentSelectedCommandParameter);
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (!(Children is null))
{
foreach (var segment in Children)
{
segment.BindingContext = BindingContext;
}
}
}
}
}