// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using Avalonia.Media; using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Base class for controls that can contain multiple children. /// /// /// Controls can be added to a by adding them to its /// collection. All children are layed out to fill the panel. /// public class Panel : Control, IPanel { /// /// Defines the property. /// public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); /// /// Initializes static members of the class. /// static Panel() { AffectsRender(BackgroundProperty); } /// /// Initializes a new instance of the class. /// public Panel() { Children.CollectionChanged += ChildrenChanged; } /// /// Gets the children of the . /// [Content] public Controls Children { get; } = new Controls(); /// /// Gets or Sets Panel background brush. /// public IBrush Background { get { return GetValue(BackgroundProperty); } set { SetValue(BackgroundProperty, value); } } /// /// Renders the visual to a . /// /// The drawing context. public override void Render(DrawingContext context) { var background = Background; if (background != null) { var renderSize = Bounds.Size; context.FillRectangle(background, new Rect(renderSize)); } base.Render(context); } /// /// Marks a property on a child as affecting the parent panel's arrangement. /// /// The properties. protected static void AffectsParentArrange(params AvaloniaProperty[] properties) where TPanel : class, IPanel { foreach (var property in properties) { property.Changed.Subscribe(AffectsParentArrangeInvalidate); } } /// /// Marks a property on a child as affecting the parent panel's measurement. /// /// The properties. protected static void AffectsParentMeasure(params AvaloniaProperty[] properties) where TPanel : class, IPanel { foreach (var property in properties) { property.Changed.Subscribe(AffectsParentMeasureInvalidate); } } /// /// Called when the collection changes. /// /// The event sender. /// The event args. protected virtual void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) { List controls; switch (e.Action) { case NotifyCollectionChangedAction.Add: controls = e.NewItems.OfType().ToList(); LogicalChildren.InsertRange(e.NewStartingIndex, controls); VisualChildren.AddRange(e.NewItems.OfType()); break; case NotifyCollectionChangedAction.Move: LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex); VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex); break; case NotifyCollectionChangedAction.Remove: controls = e.OldItems.OfType().ToList(); LogicalChildren.RemoveAll(controls); VisualChildren.RemoveAll(e.OldItems.OfType()); break; case NotifyCollectionChangedAction.Replace: for (var i = 0; i < e.OldItems.Count; ++i) { var index = i + e.OldStartingIndex; var child = (IControl)e.NewItems[i]; LogicalChildren[index] = child; VisualChildren[index] = child; } break; case NotifyCollectionChangedAction.Reset: throw new NotSupportedException(); } InvalidateMeasure(); } private static void AffectsParentArrangeInvalidate(AvaloniaPropertyChangedEventArgs e) where TPanel : class, IPanel { var control = e.Sender as IControl; var panel = control?.VisualParent as TPanel; panel?.InvalidateArrange(); } private static void AffectsParentMeasureInvalidate(AvaloniaPropertyChangedEventArgs e) where TPanel : class, IPanel { var control = e.Sender as IControl; var panel = control?.VisualParent as TPanel; panel?.InvalidateMeasure(); } } }