// 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();
}
}
}