ButtonStrip.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. using Avalonia;
  2. using Avalonia.Controls;
  3. using Avalonia.Controls.Metadata;
  4. using Avalonia.Controls.Primitives;
  5. using Avalonia.LogicalTree;
  6. using Avalonia.Media;
  7. using Avalonia.Metadata;
  8. using Avalonia.Threading;
  9. using CommunityToolkit.Mvvm.Input;
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Collections.Specialized;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using System.Windows.Input;
  19. namespace InABox.Avalonia.Components;
  20. public partial class ButtonStrip : TemplatedControl
  21. {
  22. public static StyledProperty<IBrush?> SelectedBackgroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
  23. nameof(SelectedBackground));
  24. public static StyledProperty<IBrush?> SelectedForegroundProperty = AvaloniaProperty.Register<ButtonStripItem, IBrush?>(
  25. nameof(SelectedForeground));
  26. public static StyledProperty<double> ItemSpacingProperty = AvaloniaProperty.Register<ButtonStripItem, double>(
  27. nameof(ItemSpacing));
  28. public static StyledProperty<ObservableCollection<ButtonStripItem>> ItemsProperty =
  29. AvaloniaProperty.Register<ButtonStripItem, ObservableCollection<ButtonStripItem>>(nameof(Items));
  30. public IBrush? SelectedBackground
  31. {
  32. get => GetValue(SelectedBackgroundProperty);
  33. set => SetValue(SelectedBackgroundProperty, value);
  34. }
  35. public IBrush? SelectedForeground
  36. {
  37. get => GetValue(SelectedForegroundProperty);
  38. set => SetValue(SelectedForegroundProperty, value);
  39. }
  40. public double ItemSpacing
  41. {
  42. get => GetValue(ItemSpacingProperty);
  43. set => SetValue(ItemSpacingProperty, value);
  44. }
  45. [Content]
  46. public ObservableCollection<ButtonStripItem> Items
  47. {
  48. get => GetValue(ItemsProperty);
  49. set => SetValue(ItemsProperty, value);
  50. }
  51. public ButtonStripItem? SelectedItem
  52. {
  53. get => Items.FirstOrDefault(x => x.Selected);
  54. set
  55. {
  56. foreach(var item in Items)
  57. {
  58. item.Selected = item == value;
  59. }
  60. }
  61. }
  62. public event EventHandler? SelectionChanged;
  63. static ButtonStrip()
  64. {
  65. ItemsProperty.Changed.AddClassHandler<ButtonStrip>(Items_Changed);
  66. }
  67. private static void Items_Changed(ButtonStrip strip, AvaloniaPropertyChangedEventArgs args)
  68. {
  69. strip.LogicalChildren.Clear();
  70. if (strip.Items is not null)
  71. {
  72. strip.SelectedItem = strip.Items.FirstOrDefault();
  73. strip.LogicalChildren.AddRange(strip.Items);
  74. foreach(var item in strip.Items)
  75. {
  76. item.Command = strip.ItemSelectedCommand;
  77. }
  78. }
  79. }
  80. public ButtonStrip()
  81. {
  82. Items = new();
  83. Items.CollectionChanged += Items_CollectionChanged;
  84. }
  85. private void Items_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  86. {
  87. SelectedItem ??= Items.FirstOrDefault();
  88. switch (e.Action)
  89. {
  90. case NotifyCollectionChangedAction.Add:
  91. AddControlItemsToLogicalChildren(e.NewItems);
  92. break;
  93. case NotifyCollectionChangedAction.Remove:
  94. RemoveControlItemsFromLogicalChildren(e.OldItems);
  95. break;
  96. }
  97. foreach(var item in Items)
  98. {
  99. item.Command = ItemSelectedCommand;
  100. }
  101. }
  102. private void AddControlItemsToLogicalChildren(IEnumerable? items)
  103. {
  104. if (items is null) return;
  105. List<ILogical>? toAdd = null;
  106. foreach(var i in items)
  107. {
  108. if(i is Control control && !LogicalChildren.Contains(control))
  109. {
  110. toAdd ??= new();
  111. toAdd.Add(control);
  112. }
  113. }
  114. if(toAdd is not null)
  115. {
  116. LogicalChildren.AddRange(toAdd);
  117. }
  118. }
  119. private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
  120. {
  121. if (items is null) return;
  122. List<ILogical>? toRemove = null;
  123. foreach(var i in items)
  124. {
  125. if(i is Control control)
  126. {
  127. toRemove ??= new();
  128. toRemove.Add(control);
  129. }
  130. }
  131. if(toRemove is not null)
  132. {
  133. LogicalChildren.RemoveAll(toRemove);
  134. }
  135. }
  136. [RelayCommand]
  137. private void ItemSelected(ButtonStripItem item)
  138. {
  139. var children = this.GetLogicalChildren().ToArray();
  140. SelectedItem = item;
  141. }
  142. public void AddRange(IEnumerable<string> items)
  143. {
  144. foreach(var item in items)
  145. {
  146. Items.Add(new() { Text = item });
  147. }
  148. }
  149. }
  150. [PseudoClasses(":selected")]
  151. public class ButtonStripItem : TemplatedControl
  152. {
  153. public static StyledProperty<string> TextProperty = AvaloniaProperty.Register<ButtonStripItem, string>(nameof(Text));
  154. public static StyledProperty<bool> SelectedProperty = AvaloniaProperty.Register<ButtonStripItem, bool>(nameof(Selected));
  155. public static StyledProperty<int> IndexProperty = AvaloniaProperty.Register<ButtonStripItem, int>(nameof(Index));
  156. public static StyledProperty<ICommand> CommandProperty = AvaloniaProperty.Register<ButtonStripItem, ICommand>(nameof(Command));
  157. public string Text
  158. {
  159. get => GetValue(TextProperty);
  160. set => SetValue(TextProperty, value);
  161. }
  162. public bool Selected
  163. {
  164. get => GetValue(SelectedProperty);
  165. set => SetValue(SelectedProperty, value);
  166. }
  167. public int Index
  168. {
  169. get => GetValue(IndexProperty);
  170. set => SetValue(IndexProperty, value);
  171. }
  172. public ICommand Command
  173. {
  174. get => GetValue(CommandProperty);
  175. set => SetValue(CommandProperty, value);
  176. }
  177. static ButtonStripItem()
  178. {
  179. SelectedProperty.Changed.AddClassHandler<ButtonStripItem>(Selected_Changed);
  180. }
  181. private static void Selected_Changed(ButtonStripItem item, AvaloniaPropertyChangedEventArgs args)
  182. {
  183. item.PseudoClasses.Set(":selected", item.Selected);
  184. }
  185. }