VirtualizingStackPanel.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Specialized;
  5. namespace Avalonia.Controls
  6. {
  7. public class VirtualizingStackPanel : StackPanel, IVirtualizingPanel
  8. {
  9. private double _takenSpace;
  10. private int _canBeRemoved;
  11. private double _averageItemSize;
  12. private int _averageCount;
  13. private double _pixelOffset;
  14. bool IVirtualizingPanel.IsFull
  15. {
  16. get
  17. {
  18. return Orientation == Orientation.Horizontal ?
  19. _takenSpace >= Bounds.Width :
  20. _takenSpace >= Bounds.Height;
  21. }
  22. }
  23. int IVirtualizingPanel.OverflowCount => _canBeRemoved;
  24. Orientation IVirtualizingPanel.ScrollDirection => Orientation;
  25. double IVirtualizingPanel.AverageItemSize => _averageItemSize;
  26. double IVirtualizingPanel.PixelOverflow
  27. {
  28. get
  29. {
  30. var bounds = Orientation == Orientation.Horizontal ?
  31. Bounds.Width : Bounds.Height;
  32. return Math.Max(0, (_takenSpace - _pixelOffset) - bounds);
  33. }
  34. }
  35. double IVirtualizingPanel.PixelOffset
  36. {
  37. get { return _pixelOffset; }
  38. set
  39. {
  40. if (_pixelOffset != value)
  41. {
  42. _pixelOffset = value;
  43. InvalidateArrange();
  44. }
  45. }
  46. }
  47. protected override Size ArrangeOverride(Size finalSize)
  48. {
  49. _canBeRemoved = 0;
  50. _takenSpace = 0;
  51. _averageItemSize = 0;
  52. _averageCount = 0;
  53. var result = base.ArrangeOverride(finalSize);
  54. return result;
  55. }
  56. protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
  57. {
  58. base.ChildrenChanged(sender, e);
  59. switch (e.Action)
  60. {
  61. case NotifyCollectionChangedAction.Add:
  62. foreach (IControl control in e.NewItems)
  63. {
  64. UpdateAdd(control);
  65. }
  66. break;
  67. case NotifyCollectionChangedAction.Remove:
  68. foreach (IControl control in e.OldItems)
  69. {
  70. UpdateRemove(control);
  71. }
  72. break;
  73. }
  74. }
  75. internal override void ArrangeChild(
  76. IControl child,
  77. Rect rect,
  78. Size panelSize,
  79. Orientation orientation)
  80. {
  81. if (orientation == Orientation.Vertical)
  82. {
  83. rect = new Rect(rect.X, rect.Y - _pixelOffset, rect.Width, rect.Height);
  84. child.Arrange(rect);
  85. if (rect.Y >= panelSize.Height)
  86. {
  87. ++_canBeRemoved;
  88. }
  89. if (rect.Bottom >= _takenSpace)
  90. {
  91. _takenSpace = rect.Bottom;
  92. }
  93. AddToAverageItemSize(rect.Height);
  94. }
  95. else
  96. {
  97. rect = new Rect(rect.X - _pixelOffset, rect.Y, rect.Width, rect.Height);
  98. child.Arrange(rect);
  99. if (rect.X >= panelSize.Width)
  100. {
  101. ++_canBeRemoved;
  102. }
  103. if (rect.Right >= _takenSpace)
  104. {
  105. _takenSpace = rect.Right;
  106. }
  107. AddToAverageItemSize(rect.Width);
  108. }
  109. }
  110. private void UpdateAdd(IControl child)
  111. {
  112. var bounds = Bounds;
  113. var gap = Gap;
  114. child.Measure(bounds.Size);
  115. ++_averageCount;
  116. if (Orientation == Orientation.Vertical)
  117. {
  118. var height = child.DesiredSize.Height;
  119. _takenSpace += height + gap;
  120. AddToAverageItemSize(height);
  121. }
  122. else
  123. {
  124. var width = child.DesiredSize.Width;
  125. _takenSpace += width + gap;
  126. AddToAverageItemSize(width);
  127. }
  128. }
  129. private void UpdateRemove(IControl child)
  130. {
  131. var bounds = Bounds;
  132. var gap = Gap;
  133. if (Orientation == Orientation.Vertical)
  134. {
  135. var height = child.DesiredSize.Height;
  136. _takenSpace -= height + gap;
  137. RemoveFromAverageItemSize(height);
  138. }
  139. else
  140. {
  141. var width = child.DesiredSize.Width;
  142. _takenSpace -= width + gap;
  143. RemoveFromAverageItemSize(width);
  144. }
  145. }
  146. private void AddToAverageItemSize(double value)
  147. {
  148. ++_averageCount;
  149. _averageItemSize += (value - _averageItemSize) / _averageCount;
  150. }
  151. private void RemoveFromAverageItemSize(double value)
  152. {
  153. _averageItemSize = ((_averageItemSize * _averageCount) - value) / (_averageCount - 1);
  154. --_averageCount;
  155. }
  156. }
  157. }