using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Styling;
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, IChildIndexProvider
{
///
/// Defines the property.
///
public static readonly StyledProperty BackgroundProperty =
Border.BackgroundProperty.AddOwner();
///
/// Initializes static members of the class.
///
static Panel()
{
AffectsRender(BackgroundProperty);
}
private EventHandler? _childIndexChanged;
///
/// 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); }
}
event EventHandler? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= 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.InsertRange(e.NewStartingIndex, 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();
}
_childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty);
InvalidateMeasureOnChildrenChanged();
}
private protected virtual void InvalidateMeasureOnChildrenChanged()
{
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();
}
int IChildIndexProvider.GetChildIndex(ILogical child)
{
return child is IControl control ? Children.IndexOf(control) : -1;
}
public bool TryGetTotalCount(out int count)
{
count = Children.Count;
return true;
}
}
}