forked from amkovkov/GranuSightSoftware2
feat: SectionPanel ChildrenSource and ChildrenTemplate
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Animation;
|
using Avalonia.Animation;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
@@ -64,6 +68,18 @@ public class SectionPanel : Panel
|
|||||||
get => GetValue(CanScrollBackwardProperty);
|
get => GetValue(CanScrollBackwardProperty);
|
||||||
set => SetValue(CanScrollBackwardProperty, value);
|
set => SetValue(CanScrollBackwardProperty, value);
|
||||||
}
|
}
|
||||||
|
public static readonly StyledProperty<IEnumerable?> ChildrenSourceProperty = AvaloniaProperty.Register<SectionPanel, IEnumerable?>(nameof(ChildrenSource), null, defaultBindingMode: Avalonia.Data.BindingMode.OneWay);
|
||||||
|
public IEnumerable? ChildrenSource
|
||||||
|
{
|
||||||
|
get => GetValue(ChildrenSourceProperty);
|
||||||
|
set => SetValue(ChildrenSourceProperty, value);
|
||||||
|
}
|
||||||
|
public static readonly StyledProperty<IDataTemplate?> ChildrenTemplateProperty = AvaloniaProperty.Register<ItemsControl, IDataTemplate?>(nameof(ChildrenTemplate), null);
|
||||||
|
public IDataTemplate? ChildrenTemplate
|
||||||
|
{
|
||||||
|
get => GetValue(ChildrenTemplateProperty);
|
||||||
|
set => SetValue(ChildrenTemplateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Dictionary<Control, int> _childIndeces = new Dictionary<Control, int>();
|
private readonly Dictionary<Control, int> _childIndeces = new Dictionary<Control, int>();
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
@@ -78,13 +94,153 @@ public class SectionPanel : Panel
|
|||||||
CanScrollBackward = Cyclic || CurrentIndex != 0;
|
CanScrollBackward = Cyclic || CurrentIndex != 0;
|
||||||
panel.InvalidateArrange();
|
panel.InvalidateArrange();
|
||||||
});
|
});
|
||||||
|
ChildrenSourceProperty.Changed.AddClassHandler<SectionPanel>((panel, ea) =>
|
||||||
|
{
|
||||||
|
if (panel != this)
|
||||||
|
return;
|
||||||
|
(ea.OldValue as INotifyCollectionChanged)?.CollectionChanged -= panel.OnChildrenSourceCollectionChanged;
|
||||||
|
(ea.NewValue as INotifyCollectionChanged)?.CollectionChanged += panel.OnChildrenSourceCollectionChanged;
|
||||||
|
panel.Children.Clear();
|
||||||
|
if (ea.NewValue is IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
IEnumerable<Control> controls;
|
||||||
|
if (ChildrenTemplate is null)
|
||||||
|
controls = enumerable.OfType<Control>();
|
||||||
|
else
|
||||||
|
controls = enumerable.Cast<object?>()
|
||||||
|
.Where(ChildrenTemplate.Match)
|
||||||
|
.Select(i =>
|
||||||
|
{
|
||||||
|
var control = ChildrenTemplate.Build(i);
|
||||||
|
control?.DataContext = i;
|
||||||
|
return control;
|
||||||
|
})
|
||||||
|
.OfType<Control>();
|
||||||
|
|
||||||
|
foreach (var control in controls)
|
||||||
|
panel.Children.Add(control);
|
||||||
|
}
|
||||||
|
panel.InvalidateArrange();
|
||||||
|
});
|
||||||
Children.CollectionChanged += (_, ea) =>
|
Children.CollectionChanged += (_, ea) =>
|
||||||
{
|
{
|
||||||
CanScrollForward = Cyclic || CurrentIndex != Children.Count() - 1;
|
CanScrollForward = (Cyclic && Children.Count() > 1) || CurrentIndex != Children.Count() - 1;
|
||||||
CanScrollBackward = Cyclic || CurrentIndex != 0;
|
CanScrollBackward = (Cyclic && Children.Count() > 1) || CurrentIndex != 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void OnChildrenSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs ea)
|
||||||
|
{
|
||||||
|
switch (ea.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
{
|
||||||
|
if (ea.NewItems is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var index = ea.NewStartingIndex >= 0
|
||||||
|
? ea.NewStartingIndex
|
||||||
|
: Children.Count;
|
||||||
|
|
||||||
|
IEnumerable<Control> controls;
|
||||||
|
if (ChildrenTemplate is null)
|
||||||
|
controls = ea.NewItems.OfType<Control>();
|
||||||
|
else
|
||||||
|
controls = ea.NewItems.Cast<object?>()
|
||||||
|
.Where(ChildrenTemplate.Match)
|
||||||
|
.Select(i =>
|
||||||
|
{
|
||||||
|
var control = ChildrenTemplate.Build(i);
|
||||||
|
control?.DataContext = i;
|
||||||
|
return control;
|
||||||
|
})
|
||||||
|
.OfType<Control>();
|
||||||
|
|
||||||
|
foreach (Control control in controls)
|
||||||
|
Children.Insert(index++, control);
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
{
|
||||||
|
if (ea.OldItems is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (Control control in ea.OldItems)
|
||||||
|
Children.Remove(control);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Replace:
|
||||||
|
{
|
||||||
|
if (ea.OldItems is not null)
|
||||||
|
foreach (Control control in ea.OldItems)
|
||||||
|
Children.Remove(control);
|
||||||
|
|
||||||
|
if (ea.NewItems is not null)
|
||||||
|
{
|
||||||
|
var index = ea.NewStartingIndex >= 0 ?
|
||||||
|
ea.NewStartingIndex :
|
||||||
|
Children.Count;
|
||||||
|
|
||||||
|
foreach (Control control in ea.NewItems)
|
||||||
|
Children.Insert(index++, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Move:
|
||||||
|
{
|
||||||
|
if (ea.OldItems is not IEnumerable<Control> oldControls || oldControls.Count() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var control = oldControls.First();
|
||||||
|
|
||||||
|
if (ea.OldStartingIndex >= 0)
|
||||||
|
Children.RemoveAt(ea.OldStartingIndex);
|
||||||
|
|
||||||
|
if (ea.NewStartingIndex >= 0)
|
||||||
|
Children.Insert(ea.NewStartingIndex, control);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Reset:
|
||||||
|
{
|
||||||
|
Children.Clear();
|
||||||
|
|
||||||
|
if (ChildrenSource is null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
IEnumerable<Control> controls;
|
||||||
|
if (ChildrenTemplate is null)
|
||||||
|
controls = ChildrenSource.OfType<Control>();
|
||||||
|
else
|
||||||
|
controls = ChildrenSource.Cast<object?>()
|
||||||
|
.Where(ChildrenTemplate.Match)
|
||||||
|
.Select(i =>
|
||||||
|
{
|
||||||
|
var control = ChildrenTemplate.Build(i);
|
||||||
|
control?.DataContext = i;
|
||||||
|
return control;
|
||||||
|
})
|
||||||
|
.OfType<Control>();
|
||||||
|
|
||||||
|
foreach (var control in controls)
|
||||||
|
Children.Add(control);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidateMeasure();
|
||||||
|
InvalidateArrange();
|
||||||
|
}
|
||||||
|
|
||||||
protected override Size MeasureOverride(Size availableSize)
|
protected override Size MeasureOverride(Size availableSize)
|
||||||
{
|
{
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
|
|||||||
Reference in New Issue
Block a user