ElementGroup.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using System.Collections.Generic;
  2. using System.Collections.Specialized;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Media;
  7. using HandyControl.Data;
  8. using HandyControl.Tools;
  9. namespace HandyControl.Controls;
  10. public class ElementGroup : ItemsControl
  11. {
  12. private static readonly Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>> CornerRadiusDict;
  13. public static readonly DependencyProperty OrientationProperty =
  14. StackPanel.OrientationProperty.AddOwner(typeof(ElementGroup),
  15. new FrameworkPropertyMetadata(ValueBoxes.HorizontalBox, FrameworkPropertyMetadataOptions.AffectsMeasure));
  16. public Orientation Orientation
  17. {
  18. get => (Orientation) GetValue(OrientationProperty);
  19. set => SetValue(OrientationProperty, ValueBoxes.OrientationBox(value));
  20. }
  21. public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register(
  22. nameof(Layout), typeof(LinearLayout), typeof(ElementGroup),
  23. new FrameworkPropertyMetadata(LinearLayout.Uniform, FrameworkPropertyMetadataOptions.AffectsMeasure));
  24. public LinearLayout Layout
  25. {
  26. get => (LinearLayout) GetValue(LayoutProperty);
  27. set => SetValue(LayoutProperty, value);
  28. }
  29. static ElementGroup()
  30. {
  31. var defaultCornerRadius = ResourceHelper.GetResourceInternal<CornerRadius>("DefaultCornerRadius");
  32. CornerRadiusDict = new Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>>
  33. {
  34. [Orientation.Horizontal] = new()
  35. {
  36. [ChildLocation.Single] = defaultCornerRadius,
  37. [ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, 0, 0, defaultCornerRadius.BottomLeft),
  38. [ChildLocation.Middle] = new CornerRadius(),
  39. [ChildLocation.Last] = new CornerRadius(0, defaultCornerRadius.TopRight, defaultCornerRadius.BottomRight, 0)
  40. },
  41. [Orientation.Vertical] = new()
  42. {
  43. [ChildLocation.Single] = defaultCornerRadius,
  44. [ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, defaultCornerRadius.TopRight, 0, 0),
  45. [ChildLocation.Middle] = new CornerRadius(),
  46. [ChildLocation.Last] = new CornerRadius(0, 0, defaultCornerRadius.BottomRight, defaultCornerRadius.BottomLeft)
  47. }
  48. };
  49. }
  50. protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
  51. {
  52. base.OnVisualChildrenChanged(visualAdded, visualRemoved);
  53. if (visualAdded is UIElement elementAdded)
  54. {
  55. elementAdded.GotFocus += ElementAdded_GotFocus;
  56. elementAdded.LostFocus += ElementAdded_LostFocus;
  57. elementAdded.IsVisibleChanged += ElementAdded_IsVisibleChanged;
  58. }
  59. if (visualRemoved is UIElement elementRemoved)
  60. {
  61. elementRemoved.GotFocus -= ElementAdded_GotFocus;
  62. elementRemoved.LostFocus -= ElementAdded_LostFocus;
  63. elementRemoved.IsVisibleChanged -= ElementAdded_IsVisibleChanged;
  64. }
  65. }
  66. protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) => UpdateChildrenCornerRadius();
  67. protected override void OnRender(DrawingContext drawingContext) => UpdateChildrenCornerRadius();
  68. private void UpdateChildrenCornerRadius()
  69. {
  70. var visibleChildren = GetVisibleChildrenCount();
  71. var count = visibleChildren.Count;
  72. if (count == 0)
  73. {
  74. return;
  75. }
  76. var orientation = Orientation;
  77. if (count == 1)
  78. {
  79. UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.Single]);
  80. }
  81. else
  82. {
  83. UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.First]);
  84. var childMargin = orientation == Orientation.Horizontal
  85. ? new Thickness(-1, 0, 0, 0)
  86. : new Thickness(0, -1, 0, 0);
  87. for (var childIndex = 1; childIndex < count; childIndex++)
  88. {
  89. var child = visibleChildren[childIndex];
  90. child.SetCurrentValue(MarginProperty, childMargin);
  91. UpdateChildCornerRadius(child, CornerRadiusDict[orientation][ChildLocation.Middle]);
  92. }
  93. var lastChild = visibleChildren[count - 1];
  94. lastChild.SetCurrentValue(MarginProperty, childMargin);
  95. UpdateChildCornerRadius(lastChild, CornerRadiusDict[orientation][ChildLocation.Last]);
  96. }
  97. }
  98. private List<UIElement> GetVisibleChildrenCount()
  99. {
  100. return Items.OfType<UIElement>().Where(element => element.Visibility == Visibility.Visible).ToList();
  101. }
  102. private static void UpdateChildCornerRadius(DependencyObject child, CornerRadius cornerRadius)
  103. {
  104. if (child is Border border)
  105. {
  106. border.SetCurrentValue(Border.CornerRadiusProperty, cornerRadius);
  107. }
  108. else
  109. {
  110. BorderElement.SetCornerRadius(child, cornerRadius);
  111. }
  112. }
  113. private void ElementAdded_LostFocus(object sender, RoutedEventArgs e) => ResetElementZIndex(e.OriginalSource);
  114. private void ElementAdded_GotFocus(object sender, RoutedEventArgs e) => MaximizeElementZIndex(e.OriginalSource);
  115. private static void ResetElementZIndex(object element) => Panel.SetZIndex((UIElement) element, 0);
  116. private static void MaximizeElementZIndex(object element) => Panel.SetZIndex((UIElement) element, int.MaxValue);
  117. private void ElementAdded_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) => UpdateChildrenCornerRadius();
  118. private enum ChildLocation
  119. {
  120. Single,
  121. First,
  122. Middle,
  123. Last
  124. }
  125. }