220 lines
7.4 KiB
C#
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);
|
|
}
|
|
} |