| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.Metadata;
- using Avalonia.Controls.Primitives;
- using Avalonia.LogicalTree;
- using Avalonia.Media;
- using Avalonia.Metadata;
- using CommunityToolkit.Mvvm.Input;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Collections.Specialized;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Input;
- namespace InABox.Avalonia.Components;
- [TemplatePart(Name = "PART_Grid")]
- public partial class Accordion : TemplatedControl
- {
- public static readonly StyledProperty<ObservableCollection<AccordionItem>> ItemsProperty =
- AvaloniaProperty.Register<Accordion, ObservableCollection<AccordionItem>>(nameof(Items));
-
- public static readonly StyledProperty<object?> SelectedItemProperty =
- AvaloniaProperty.Register<Accordion, object?>(nameof(SelectedItem));
- public static readonly DirectProperty<Accordion, int> SelectedIndexProperty =
- AvaloniaProperty.RegisterDirect<Accordion, int>(nameof(SelectedIndex), x => x.SelectedIndex, (x, v) => x.SelectedIndex = v);
- [Content]
- public ObservableCollection<AccordionItem> Items
- {
- get => GetValue(ItemsProperty);
- set => SetValue(ItemsProperty, value);
- }
- private AccordionItem? SelectedButton
- {
- get => Items.FirstOrDefault(x => x.Selected);
- set
- {
- var i = 0;
- var index = -1;
- foreach(var item in Items)
- {
- item.Selected = item == value;
- if(item == value)
- {
- index = i;
- }
- if(Grid is not null)
- {
- Grid.RowDefinitions[i].Height = item == value ? GridLength.Star : GridLength.Auto;
- }
- ++i;
- }
- SelectedItem = value?.Tag;
- }
- }
- public object? SelectedItem
- {
- get => GetValue(SelectedItemProperty);
- private set => SetValue(SelectedItemProperty, value);
- }
- public int SelectedIndex
- {
- get => SelectedButton != null && Items.Contains(SelectedButton) ? Items.IndexOf(SelectedButton) : -1;
- set => SelectedButton = value > -1 && value < Items.Count ? Items[value] : null;
- }
- public event EventHandler? SelectionChanged;
- private Grid? Grid;
- static Accordion()
- {
- ItemsProperty.Changed.AddClassHandler<Accordion>(Items_Changed);
- }
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- base.OnApplyTemplate(e);
-
- Grid = e.NameScope.Get<Grid>("PART_Grid");
- if(Grid is not null)
- {
- foreach(var item in Items)
- {
- Grid.SetRow(item, Grid.RowDefinitions.Count);
- Grid.RowDefinitions.Add(new(item.Selected ? GridLength.Star : GridLength.Auto));
- Grid.Children.Add(item);
- }
- }
- }
- private static void Items_Changed(Accordion strip, AvaloniaPropertyChangedEventArgs args)
- {
- strip.LogicalChildren.Clear();
- strip.Grid?.Children.Clear();
- if (strip.Items is not null)
- {
- strip.SelectedButton = strip.Items.FirstOrDefault();
- strip.LogicalChildren.AddRange(strip.Items);
- foreach(var item in strip.Items)
- {
- item.Command = strip.ItemSelectedCommand;
- if(strip.Grid is not null)
- {
- Grid.SetRow(item, strip.Grid.RowDefinitions.Count);
- strip.Grid.RowDefinitions.Add(new(item.Selected ? GridLength.Star : GridLength.Auto));
- strip.Grid.Children.Add(item);
- }
- }
- }
- }
- public Accordion()
- {
- Items = new();
- Items.CollectionChanged += Items_CollectionChanged;
- }
- private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
- {
- SelectedButton ??= Items.FirstOrDefault();
- switch (e.Action)
- {
- case NotifyCollectionChangedAction.Add:
- AddControlItemsToLogicalChildren(e.NewItems);
- break;
- case NotifyCollectionChangedAction.Remove:
- RemoveControlItemsFromLogicalChildren(e.OldItems);
- break;
- }
- foreach(var item in Items)
- {
- item.Command = ItemSelectedCommand;
- }
- }
- private void AddControlItemsToLogicalChildren(IEnumerable? items)
- {
- if (items is null) return;
- List<Control>? toAdd = null;
- foreach(var i in items)
- {
- if(i is Control control && !LogicalChildren.Contains(control))
- {
- toAdd ??= new();
- toAdd.Add(control);
- }
- }
- if(toAdd is not null)
- {
- LogicalChildren.AddRange(toAdd);
- if(Grid is not null)
- {
- foreach(var item in toAdd)
- {
- Grid.SetRow(item, Grid.RowDefinitions.Count);
- Grid.RowDefinitions.Add(new(item is AccordionItem aItem && aItem.Selected ? GridLength.Star : GridLength.Auto));
- Grid.Children.Add(item);
- }
- }
- }
- }
- private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
- {
- if (items is null) return;
- List<Control>? toRemove = null;
- foreach(var i in items)
- {
- if(i is Control control)
- {
- toRemove ??= new();
- toRemove.Add(control);
- }
- }
- if(toRemove is not null)
- {
- foreach(var item in toRemove)
- {
- var index = LogicalChildren.IndexOf(item);
- if(index != -1)
- {
- Grid?.RowDefinitions.RemoveAt(index);
- Grid?.Children.Remove(item);
- }
- }
- LogicalChildren.RemoveAll(toRemove);
- if(Grid is not null)
- {
- var i = 0;
- foreach(var item in LogicalChildren)
- {
- if(item is Control control)
- {
- Grid.SetRow(control, i);
- ++i;
- }
- }
- }
- }
- }
- [RelayCommand]
- private void ItemSelected(AccordionItem item)
- {
- var children = this.GetLogicalChildren().ToArray();
- SelectedButton = item;
- SelectionChanged?.Invoke(this, new());
- }
- }
- [PseudoClasses(":selected")]
- public class AccordionItem : TemplatedControl
- {
- public static readonly StyledProperty<string> TextProperty = AvaloniaProperty.Register<AccordionItem, string>(nameof(Text));
- public static readonly StyledProperty<object?> ContentProperty = AvaloniaProperty.Register<AccordionItem, object?>(nameof(Content));
- public static readonly StyledProperty<bool> SelectedProperty = AvaloniaProperty.Register<AccordionItem, bool>(nameof(Selected));
- public static readonly StyledProperty<int> IndexProperty = AvaloniaProperty.Register<AccordionItem, int>(nameof(Index));
- public static readonly StyledProperty<ICommand> CommandProperty = AvaloniaProperty.Register<AccordionItem, ICommand>(nameof(Command));
- public string Text
- {
- get => GetValue(TextProperty);
- set => SetValue(TextProperty, value);
- }
- [Content]
- public object? Content
- {
- get => GetValue(ContentProperty);
- set => SetValue(ContentProperty, value);
- }
- public bool Selected
- {
- get => GetValue(SelectedProperty);
- set => SetValue(SelectedProperty, value);
- }
- public int Index
- {
- get => GetValue(IndexProperty);
- set => SetValue(IndexProperty, value);
- }
- public ICommand Command
- {
- get => GetValue(CommandProperty);
- set => SetValue(CommandProperty, value);
- }
- static AccordionItem()
- {
- SelectedProperty.Changed.AddClassHandler<AccordionItem>(Selected_Changed);
- }
- private static void Selected_Changed(AccordionItem item, AvaloniaPropertyChangedEventArgs args)
- {
- item.PseudoClasses.Set(":selected", item.Selected);
- }
- }
|