diff --git a/demo/Ursa.Demo/Pages/TimelineDemo.axaml b/demo/Ursa.Demo/Pages/TimelineDemo.axaml
new file mode 100644
index 0000000..674a2c4
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TimelineDemo.axaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/TimelineDemo.axaml.cs b/demo/Ursa.Demo/Pages/TimelineDemo.axaml.cs
new file mode 100644
index 0000000..fe07b76
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/TimelineDemo.axaml.cs
@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Ursa.Demo.ViewModels;
+
+namespace Ursa.Demo.Pages;
+
+public partial class TimelineDemo : UserControl
+{
+ public TimelineDemo()
+ {
+ InitializeComponent();
+ this.DataContext = new TimelineDemoViewModel();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs
new file mode 100644
index 0000000..29f72f7
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs
@@ -0,0 +1,110 @@
+using System;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public class TimelineDemoViewModel: ObservableObject
+{
+ public TimelineItemViewModel[] Items { get; } =
+ {
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "yyyy-MM-dd HH:mm:ss",
+ Description = "Item 1",
+ Content = "First"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 2",
+ Content = "Content 2"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 3",
+ Content = "Content 3"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 4",
+ Content = "Content 4"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 5",
+ Content = "Content 5"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 6",
+ Content = "Content 6"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 7",
+ Content = "Content 7"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 8",
+ Content = "Content 8"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 9",
+ Content = "Content 9"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 10",
+ Content = "Content 10"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 11",
+ Content = "Content 11"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 12",
+ Content = "Content 12"
+ },
+ new()
+ {
+ Time = DateTime.Now,
+ TimeFormat = "HH:mm:ss",
+ Description = "Item 13",
+ Content = "Last"
+ }
+ };
+}
+
+public class TimelineItemViewModel: ObservableObject
+{
+ public DateTime Time { get; set; }
+ public string? TimeFormat { get; set; }
+ public string? Description { get; set; }
+ public string? Content { get; set; }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Views/MainWindow.axaml b/demo/Ursa.Demo/Views/MainWindow.axaml
index de1f2dd..9ba93b8 100644
--- a/demo/Ursa.Demo/Views/MainWindow.axaml
+++ b/demo/Ursa.Demo/Views/MainWindow.axaml
@@ -35,6 +35,9 @@
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/Timeline.axaml b/src/Ursa.Themes.Semi/Controls/Timeline.axaml
new file mode 100644
index 0000000..002d236
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/Timeline.axaml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index 4d15016..7c08f47 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -5,5 +5,6 @@
+
diff --git a/src/Ursa/Controls/Timeline/Timeline.cs b/src/Ursa/Controls/Timeline/Timeline.cs
index 34daef6..23e52dc 100644
--- a/src/Ursa/Controls/Timeline/Timeline.cs
+++ b/src/Ursa/Controls/Timeline/Timeline.cs
@@ -1,6 +1,11 @@
+using System.Collections.Specialized;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.ComTypes;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Generators;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
namespace Ursa.Controls;
@@ -17,33 +22,33 @@ public class Timeline: ItemsControl
set => SetValue(ItemDescriptionTemplateProperty, value);
}
- protected override bool IsItemItsOwnContainerOverride(Control item)
+ public Timeline()
{
- return item is TimelineItem;
+ ItemsView.CollectionChanged+=ItemsViewOnCollectionChanged;
}
- protected override Control CreateContainerForItemOverride()
+ private void ItemsViewOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
- return new TimelineItem();
+ RefreshTimelineItems();
}
-
- protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
- base.PrepareContainerForItemOverride(container, item, index);
- if (container is TimelineItem c )
+ base.OnPropertyChanged(change);
+ RefreshTimelineItems();
+ }
+
+ private void RefreshTimelineItems()
+ {
+ for (int i = 0; i < this.LogicalChildren.Count; i++)
{
- if (item is ITimelineItemData data)
+ if (this.LogicalChildren[i] is TimelineItem t)
{
- c[TimelineItem.TimeProperty] = data;
- c[ContentControl.ContentProperty] = data.Content;
- c[TimelineItem.DescriptionProperty] = data.Description;
- if(ItemTemplate is {}) c[ContentControl.ContentTemplateProperty] = this.ItemTemplate;
- if(ItemDescriptionTemplate is {}) c[TimelineItem.DescriptionTemplateProperty] = this.ItemDescriptionTemplate;
+ t.SetPosition(i == 0, i == this.LogicalChildren.Count - 1);
}
- else
+ else if (this.LogicalChildren[i] is ContentPresenter { Child: TimelineItem t2 })
{
- c.Content = item;
- if (ItemTemplate is { }) c[ContentControl.ContentTemplateProperty] = this.ItemTemplate;
+ t2.SetPosition(i == 0, i == this.LogicalChildren.Count - 1);
}
}
}
diff --git a/src/Ursa/Controls/Timeline/TimelineFormatConverter.cs b/src/Ursa/Controls/Timeline/TimelineFormatConverter.cs
new file mode 100644
index 0000000..48cdc8f
--- /dev/null
+++ b/src/Ursa/Controls/Timeline/TimelineFormatConverter.cs
@@ -0,0 +1,17 @@
+using System.Globalization;
+using Avalonia;
+using Avalonia.Data.Converters;
+
+namespace Ursa.Controls;
+
+public class TimelineFormatConverter: IMultiValueConverter
+{
+ public object? Convert(IList