Files
Ursa.Avalonia/src/Ursa/Controls/Timeline/Timeline.cs
2024-01-08 23:37:20 +08:00

220 lines
7.4 KiB
C#

using System.Collections.Specialized;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Metadata;
namespace Ursa.Controls;
public class Timeline: ItemsControl
{
private static readonly FuncTemplate<Panel?> DefaultPanel = new((Func<Panel>)(() => new TimelinePanel()));
public static readonly StyledProperty<IBinding?> IconMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
nameof(IconMemberBinding));
[AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IBinding? IconMemberBinding
{
get => GetValue(IconMemberBindingProperty);
set => SetValue(IconMemberBindingProperty, value);
}
public static readonly StyledProperty<IBinding?> HeaderMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
nameof(HeaderMemberBinding));
[AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IBinding? HeaderMemberBinding
{
get => GetValue(HeaderMemberBindingProperty);
set => SetValue(HeaderMemberBindingProperty, value);
}
public static readonly StyledProperty<IBinding?> ContentMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
nameof(ContentMemberBinding));
[AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IBinding? ContentMemberBinding
{
get => GetValue(ContentMemberBindingProperty);
set => SetValue(ContentMemberBindingProperty, value);
}
public static readonly StyledProperty<IDataTemplate?> IconTemplateProperty = AvaloniaProperty.Register<Timeline, IDataTemplate?>(
nameof(IconTemplate));
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IDataTemplate? IconTemplate
{
get => GetValue(IconTemplateProperty);
set => SetValue(IconTemplateProperty, value);
}
public static readonly StyledProperty<IDataTemplate?> DescriptionTemplateProperty = AvaloniaProperty.Register<Timeline, IDataTemplate?>(
nameof(DescriptionTemplate));
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IDataTemplate? DescriptionTemplate
{
get => GetValue(DescriptionTemplateProperty);
set => SetValue(DescriptionTemplateProperty, value);
}
public static readonly StyledProperty<IBinding?> TimeMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
nameof(TimeMemberBinding));
[AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IBinding? TimeMemberBinding
{
get => GetValue(TimeMemberBindingProperty);
set => SetValue(TimeMemberBindingProperty, value);
}
public static readonly StyledProperty<string?> TimeFormatProperty = AvaloniaProperty.Register<Timeline, string?>(
nameof(TimeFormat), defaultValue:"yyyy-MM-dd HH:mm:ss");
public string? TimeFormat
{
get => GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
public static readonly StyledProperty<TimelineDisplayMode> ModeProperty = AvaloniaProperty.Register<Timeline, TimelineDisplayMode>(
nameof(Mode));
public TimelineDisplayMode Mode
{
get => GetValue(ModeProperty);
set => SetValue(ModeProperty, value);
}
static Timeline()
{
ItemsPanelProperty.OverrideDefaultValue<Timeline>(DefaultPanel);
ModeProperty.Changed.AddClassHandler<Timeline, TimelineDisplayMode>((t, e) => { t.OnDisplayModeChanged(e); });
}
private void OnDisplayModeChanged(AvaloniaPropertyChangedEventArgs<TimelineDisplayMode> e)
{
if (this.ItemsPanelRoot is TimelinePanel panel)
{
panel.Mode = e.NewValue.Value;
SetItemMode();
}
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
recycleKey = null;
return item is not TimelineItem;
}
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
if (item is TimelineItem t) return t;
return new TimelineItem();
}
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
{
base.PrepareContainerForItemOverride(container, item, index);
if (container is TimelineItem t)
{
bool start = index == 0;
bool end = index == ItemCount - 1;
t.SetEnd(start, end);
if (IconMemberBinding is not null)
{
t.Bind(TimelineItem.IconProperty, IconMemberBinding);
}
if (HeaderMemberBinding != null)
{
t.Bind(HeaderedContentControl.HeaderProperty, HeaderMemberBinding);
}
if (ContentMemberBinding != null)
{
t.Bind(ContentControl.ContentProperty, ContentMemberBinding);
}
if (TimeMemberBinding != null)
{
t.Bind(TimelineItem.TimeProperty, TimeMemberBinding);
}
t.SetIfUnset(TimelineItem.TimeFormatProperty, TimeFormat);
t.SetIfUnset(TimelineItem.IconTemplateProperty, IconTemplate);
t.SetIfUnset(HeaderedContentControl.HeaderTemplateProperty, ItemTemplate);
t.SetIfUnset(ContentControl.ContentTemplateProperty, DescriptionTemplate);
}
}
protected override Size ArrangeOverride(Size finalSize)
{
var panel = this.ItemsPanelRoot as TimelinePanel;
panel.Mode = this.Mode;
SetItemMode();
return base.ArrangeOverride(finalSize);
}
private void SetItemMode()
{
if (ItemsPanelRoot is TimelinePanel panel)
{
var items = panel.Children.OfType<TimelineItem>();
if (Mode == TimelineDisplayMode.Left)
{
foreach (var item in items)
{
SetIfUnset(item, TimelineItem.PositionProperty, TimelineItemPosition.Left);
}
}
else if (Mode == TimelineDisplayMode.Right)
{
foreach (var item in items)
{
SetIfUnset(item, TimelineItem.PositionProperty, TimelineItemPosition.Right);
}
}
else if (Mode == TimelineDisplayMode.Center)
{
foreach (var item in items)
{
SetIfUnset(item, TimelineItem.PositionProperty, TimelineItemPosition.Separate);
}
}
else if (Mode == TimelineDisplayMode.Alternate)
{
bool left = false;
foreach (var item in items)
{
if (left)
{
SetIfUnset(item, TimelineItem.PositionProperty, TimelineItemPosition.Left);
}
else
{
SetIfUnset(item, TimelineItem.PositionProperty, TimelineItemPosition.Right);
}
left = !left;
}
}
}
}
private void SetIfUnset<T>(AvaloniaObject target, StyledProperty<T> property, T value)
{
if (!target.IsSet(property))
target.SetCurrentValue(property, value);
}
}