Panel.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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.Generic;
  5. using System.Collections.Specialized;
  6. using System.Linq;
  7. using Avalonia.Media;
  8. using Avalonia.Metadata;
  9. namespace Avalonia.Controls
  10. {
  11. /// <summary>
  12. /// Base class for controls that can contain multiple children.
  13. /// </summary>
  14. /// <remarks>
  15. /// Controls can be added to a <see cref="Panel"/> by adding them to its <see cref="Children"/>
  16. /// collection. All children are layed out to fill the panel.
  17. /// </remarks>
  18. public class Panel : Control, IPanel
  19. {
  20. /// <summary>
  21. /// Defines the <see cref="Background"/> property.
  22. /// </summary>
  23. public static readonly StyledProperty<IBrush> BackgroundProperty =
  24. Border.BackgroundProperty.AddOwner<Panel>();
  25. /// <summary>
  26. /// Initializes static members of the <see cref="Panel"/> class.
  27. /// </summary>
  28. static Panel()
  29. {
  30. AffectsRender<Panel>(BackgroundProperty);
  31. }
  32. /// <summary>
  33. /// Initializes a new instance of the <see cref="Panel"/> class.
  34. /// </summary>
  35. public Panel()
  36. {
  37. Children.CollectionChanged += ChildrenChanged;
  38. }
  39. /// <summary>
  40. /// Gets the children of the <see cref="Panel"/>.
  41. /// </summary>
  42. [Content]
  43. public Controls Children { get; } = new Controls();
  44. /// <summary>
  45. /// Gets or Sets Panel background brush.
  46. /// </summary>
  47. public IBrush Background
  48. {
  49. get { return GetValue(BackgroundProperty); }
  50. set { SetValue(BackgroundProperty, value); }
  51. }
  52. /// <summary>
  53. /// Renders the visual to a <see cref="DrawingContext"/>.
  54. /// </summary>
  55. /// <param name="context">The drawing context.</param>
  56. public override void Render(DrawingContext context)
  57. {
  58. var background = Background;
  59. if (background != null)
  60. {
  61. var renderSize = Bounds.Size;
  62. context.FillRectangle(background, new Rect(renderSize));
  63. }
  64. base.Render(context);
  65. }
  66. /// <summary>
  67. /// Marks a property on a child as affecting the parent panel's arrangement.
  68. /// </summary>
  69. /// <param name="properties">The properties.</param>
  70. protected static void AffectsParentArrange<TPanel>(params AvaloniaProperty[] properties)
  71. where TPanel : class, IPanel
  72. {
  73. foreach (var property in properties)
  74. {
  75. property.Changed.Subscribe(AffectsParentArrangeInvalidate<TPanel>);
  76. }
  77. }
  78. /// <summary>
  79. /// Marks a property on a child as affecting the parent panel's measurement.
  80. /// </summary>
  81. /// <param name="properties">The properties.</param>
  82. protected static void AffectsParentMeasure<TPanel>(params AvaloniaProperty[] properties)
  83. where TPanel : class, IPanel
  84. {
  85. foreach (var property in properties)
  86. {
  87. property.Changed.Subscribe(AffectsParentMeasureInvalidate<TPanel>);
  88. }
  89. }
  90. /// <summary>
  91. /// Called when the <see cref="Children"/> collection changes.
  92. /// </summary>
  93. /// <param name="sender">The event sender.</param>
  94. /// <param name="e">The event args.</param>
  95. protected virtual void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
  96. {
  97. List<Control> controls;
  98. switch (e.Action)
  99. {
  100. case NotifyCollectionChangedAction.Add:
  101. controls = e.NewItems.OfType<Control>().ToList();
  102. LogicalChildren.InsertRange(e.NewStartingIndex, controls);
  103. VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems.OfType<Visual>());
  104. break;
  105. case NotifyCollectionChangedAction.Move:
  106. LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
  107. VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
  108. break;
  109. case NotifyCollectionChangedAction.Remove:
  110. controls = e.OldItems.OfType<Control>().ToList();
  111. LogicalChildren.RemoveAll(controls);
  112. VisualChildren.RemoveAll(e.OldItems.OfType<Visual>());
  113. break;
  114. case NotifyCollectionChangedAction.Replace:
  115. for (var i = 0; i < e.OldItems.Count; ++i)
  116. {
  117. var index = i + e.OldStartingIndex;
  118. var child = (IControl)e.NewItems[i];
  119. LogicalChildren[index] = child;
  120. VisualChildren[index] = child;
  121. }
  122. break;
  123. case NotifyCollectionChangedAction.Reset:
  124. throw new NotSupportedException();
  125. }
  126. InvalidateMeasure();
  127. }
  128. private static void AffectsParentArrangeInvalidate<TPanel>(AvaloniaPropertyChangedEventArgs e)
  129. where TPanel : class, IPanel
  130. {
  131. var control = e.Sender as IControl;
  132. var panel = control?.VisualParent as TPanel;
  133. panel?.InvalidateArrange();
  134. }
  135. private static void AffectsParentMeasureInvalidate<TPanel>(AvaloniaPropertyChangedEventArgs e)
  136. where TPanel : class, IPanel
  137. {
  138. var control = e.Sender as IControl;
  139. var panel = control?.VisualParent as TPanel;
  140. panel?.InvalidateMeasure();
  141. }
  142. }
  143. }