Stepper.axaml.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Linq;
  6. using Avalonia;
  7. using Avalonia.Controls;
  8. using Avalonia.Controls.Primitives;
  9. using Avalonia.Interactivity;
  10. using Avalonia.Layout;
  11. using Avalonia.Markup.Xaml.MarkupExtensions;
  12. using Avalonia.Media;
  13. using Avalonia.Threading;
  14. using SukiUI.Content;
  15. namespace SukiUI.Controls
  16. {
  17. public class Stepper : TemplatedControl
  18. {
  19. public static readonly StyledProperty<bool> AlternativeStyleProperty =
  20. AvaloniaProperty.Register<Stepper, bool>(nameof(AlternativeStyle));
  21. public static readonly StyledProperty<int> IndexProperty =
  22. AvaloniaProperty.Register<Stepper, int>(nameof(Index));
  23. public static readonly StyledProperty<IEnumerable?> StepsProperty =
  24. AvaloniaProperty.Register<Stepper, IEnumerable?>(nameof(Steps));
  25. public bool AlternativeStyle
  26. {
  27. get => GetValue(AlternativeStyleProperty);
  28. set => SetValue(AlternativeStyleProperty, value);
  29. }
  30. public int Index
  31. {
  32. get => GetValue(IndexProperty);
  33. set => SetValue(IndexProperty, value);
  34. }
  35. public IEnumerable? Steps
  36. {
  37. get => GetValue(StepsProperty);
  38. set => SetValue(StepsProperty, value);
  39. }
  40. private Grid? _grid;
  41. protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
  42. {
  43. base.OnApplyTemplate(e);
  44. if (e.NameScope.Get<Grid>("PART_GridStepper") is not { } grid)
  45. {
  46. return;
  47. }
  48. _grid = grid;
  49. StepsChangedHandler(Steps);
  50. }
  51. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  52. {
  53. base.OnPropertyChanged(change);
  54. if (change.Property == IndexProperty || change.Property == StepsProperty)
  55. StepsChangedHandler(Steps);
  56. }
  57. private void StepsChangedHandler(IEnumerable? newSteps)
  58. {
  59. if (newSteps is null)
  60. {
  61. return;
  62. }
  63. if (newSteps is not IEnumerable<object> stepsEnumerable)
  64. {
  65. return;
  66. }
  67. var steps = stepsEnumerable.ToArray();
  68. if (AlternativeStyle)
  69. {
  70. UpdateAlternate(steps);
  71. }
  72. else
  73. {
  74. Update(steps);
  75. }
  76. if (newSteps is INotifyCollectionChanged notify)
  77. {
  78. notify.CollectionChanged += (_, _) => Update(steps);
  79. }
  80. }
  81. #region StepperBaseStyle
  82. private void Update(object[] steps)
  83. {
  84. if (_grid is null)
  85. {
  86. return;
  87. }
  88. _grid.Children.Clear();
  89. SetColumnDefinitions(_grid, steps);
  90. for (var i = 0; i < steps.Length; i++)
  91. {
  92. AddStep(steps[i], i, _grid, steps.Length);
  93. }
  94. }
  95. private void SetColumnDefinitions(Grid grid, object[] steps)
  96. {
  97. var columns = new ColumnDefinitions();
  98. for (var i = 0; i < steps.Length; i++)
  99. {
  100. columns.Add(new ColumnDefinition());
  101. }
  102. grid.ColumnDefinitions = columns;
  103. }
  104. private void AddStep(object step, int index, Grid grid, int stepCount)
  105. {
  106. var griditem = new Grid
  107. {
  108. ColumnDefinitions = new ColumnDefinitions
  109. { new(GridLength.Auto), new(GridLength.Star), new(GridLength.Auto) }
  110. };
  111. var icon = new PathIcon
  112. { Height = 10, Width = 10, Data = Icons.ChevronRight, Margin = new Thickness(0, 0, 20, 0), Classes = { "Flippable" } };
  113. if (index == stepCount - 1)
  114. {
  115. icon.IsVisible = false;
  116. }
  117. Grid.SetColumn(icon, 2);
  118. griditem.Children.Add(icon);
  119. var circle = new Border
  120. {
  121. Margin = new Thickness(0, 0, 0, 2),
  122. Height = 24,
  123. Width = 24,
  124. CornerRadius = new CornerRadius(25),
  125. HorizontalAlignment = HorizontalAlignment.Center,
  126. VerticalAlignment = VerticalAlignment.Center
  127. };
  128. if (index <= Index)
  129. {
  130. circle[!BackgroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  131. circle.BorderThickness = new Thickness(0);
  132. circle.Child = new TextBlock
  133. {
  134. VerticalAlignment = VerticalAlignment.Center,
  135. HorizontalAlignment = HorizontalAlignment.Center,
  136. Text = (index + 1).ToString(),
  137. FontSize = 13,
  138. Foreground = Brushes.White, TextWrapping = TextWrapping.Wrap
  139. };
  140. }
  141. else
  142. {
  143. circle[!BackgroundProperty] = new DynamicResourceExtension("SukiControlBorderBrush");
  144. circle.BorderThickness = new Thickness(0);
  145. circle.Child = new TextBlock
  146. {
  147. VerticalAlignment = VerticalAlignment.Center,
  148. HorizontalAlignment = HorizontalAlignment.Center,
  149. Text = (index + 1).ToString(),
  150. FontSize = 13,
  151. Foreground = Brushes.White, TextWrapping = TextWrapping.Wrap
  152. };
  153. }
  154. Grid.SetColumn(circle, 0);
  155. griditem.Children.Add(circle);
  156. Control content = step switch
  157. {
  158. string s => new TextBlock
  159. {
  160. FontWeight = index <= Index
  161. ? TryGetResource("DefaultDemiBold", ActualThemeVariant, out var fontWeight)
  162. ? (FontWeight)fontWeight!
  163. : FontWeight.Bold
  164. : FontWeight.Normal,
  165. Margin = new Thickness(10, 0, 0, 0),
  166. Text = s,
  167. VerticalAlignment = VerticalAlignment.Center,
  168. HorizontalAlignment = HorizontalAlignment.Left, TextWrapping = TextWrapping.Wrap
  169. },
  170. _ => new ContentControl { Content = step }
  171. };
  172. Grid.SetColumn(content, 1);
  173. griditem.Children.Add(content);
  174. Grid.SetColumn(griditem, index);
  175. grid.Children.Add(griditem);
  176. }
  177. #endregion
  178. #region StepperAlternateStyle
  179. public void UpdateAlternate(object[] steps)
  180. {
  181. if (_grid is null)
  182. {
  183. return;
  184. }
  185. _grid.Children.Clear();
  186. SetColumnDefinitionsAlternate(_grid);
  187. for (var i = 0; i < steps.Length; i++)
  188. {
  189. AddStepAlternate(steps[i], i, _grid, steps);
  190. }
  191. }
  192. private void SetColumnDefinitionsAlternate(Grid grid)
  193. {
  194. var columns = new ColumnDefinitions();
  195. foreach (var s in Steps)
  196. {
  197. columns.Add(new ColumnDefinition());
  198. }
  199. grid.ColumnDefinitions = columns;
  200. }
  201. private void AddStepAlternate(object step, int index, Grid grid, object[] steps)
  202. {
  203. var gridItem = new Grid { ColumnDefinitions = new ColumnDefinitions { new(), new() } };
  204. var line = new Border
  205. {
  206. CornerRadius = new CornerRadius(3), Margin = new Thickness(-5, 0, 23, 0),
  207. Height = 2,
  208. HorizontalAlignment = HorizontalAlignment.Stretch,
  209. VerticalAlignment = VerticalAlignment.Center,
  210. [!BackgroundProperty] = new DynamicResourceExtension("SukiControlBorderBrush")
  211. };
  212. var line1 = new Border
  213. {
  214. CornerRadius = new CornerRadius(3), Margin = new Thickness(23, 0, -5, 0),
  215. Height = 2, HorizontalAlignment = HorizontalAlignment.Stretch,
  216. VerticalAlignment = VerticalAlignment.Center,
  217. [!BackgroundProperty] = new DynamicResourceExtension("SukiControlBorderBrush")
  218. };
  219. if (index == 0)
  220. {
  221. line.IsVisible = false;
  222. }
  223. if (index == steps.Length - 1)
  224. {
  225. line1.IsVisible = false;
  226. }
  227. if (index == Index)
  228. {
  229. line[!BackgroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  230. }
  231. if (index < Index)
  232. {
  233. line1[!BackgroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  234. line[!BackgroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  235. }
  236. Grid.SetColumn(line, 0);
  237. Grid.SetColumn(line1, 1);
  238. gridItem.Children.Add(line);
  239. gridItem.Children.Add(line1);
  240. var gridBorder = new Grid();
  241. var circle = new Border
  242. {
  243. Margin = new Thickness(0, 0, 0, 2),
  244. Height = 30,
  245. Width = 30,
  246. CornerRadius = new CornerRadius(25),
  247. HorizontalAlignment = HorizontalAlignment.Center,
  248. VerticalAlignment = VerticalAlignment.Center
  249. };
  250. if (index == Index)
  251. {
  252. circle[!BackgroundProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  253. circle.BorderThickness = new Thickness(0);
  254. circle.Child = new TextBlock
  255. {
  256. VerticalAlignment = VerticalAlignment.Center,
  257. HorizontalAlignment = HorizontalAlignment.Center,
  258. Text = (index + 1).ToString(),
  259. Foreground = Brushes.White
  260. };
  261. }
  262. else if (index < Index)
  263. {
  264. circle.Background = Brushes.Transparent;
  265. circle.BorderThickness = new Thickness(1.5);
  266. circle[!BorderBrushProperty] = new DynamicResourceExtension("SukiPrimaryColor");
  267. circle.Child = new TextBlock
  268. {
  269. VerticalAlignment = VerticalAlignment.Center,
  270. HorizontalAlignment = HorizontalAlignment.Center,
  271. Text = (index + 1).ToString(),
  272. [!ForegroundProperty] = new DynamicResourceExtension("SukiPrimaryColor")
  273. };
  274. }
  275. else
  276. {
  277. circle.Background = Brushes.Transparent;
  278. circle.BorderThickness = new Thickness(1.5);
  279. circle[!BorderBrushProperty] = new DynamicResourceExtension("SukiControlBorderBrush");
  280. circle.Child = new TextBlock
  281. {
  282. VerticalAlignment = VerticalAlignment.Center,
  283. HorizontalAlignment = HorizontalAlignment.Center,
  284. Text = (index + 1).ToString(),
  285. [!ForegroundProperty] = new DynamicResourceExtension("SukiControlBorderBrush")
  286. };
  287. }
  288. gridBorder.Children.Add(circle);
  289. gridBorder.Children.Add(new TextBlock
  290. {
  291. FontWeight = index == Index ? FontWeight.Medium : FontWeight.Normal,
  292. Text = step.ToString(), VerticalAlignment = VerticalAlignment.Center,
  293. HorizontalAlignment = HorizontalAlignment.Center, Margin = new Thickness(0, 55, 0, 0)
  294. });
  295. Grid.SetColumn(gridItem, index);
  296. Grid.SetColumn(gridBorder, index);
  297. grid.Children.Add(gridItem);
  298. grid.Children.Add(gridBorder);
  299. }
  300. #endregion
  301. }
  302. }