feat: add demo page, prepare collection changed behavior.
This commit is contained in:
25
demo/Ursa.Demo/Pages/TimelineDemo.axaml
Normal file
25
demo/Ursa.Demo/Pages/TimelineDemo.axaml
Normal file
@@ -0,0 +1,25 @@
|
||||
<UserControl
|
||||
x:Class="Ursa.Demo.Pages.TimelineDemo"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
xmlns:viewModels="clr-namespace:Ursa.Demo.ViewModels"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="viewModels:TimelineDemoViewModel"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<u:Timeline ItemsSource="{Binding Items}">
|
||||
<u:Timeline.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:TimelineItemViewModel">
|
||||
<u:TimelineItem
|
||||
Content="{Binding Content}"
|
||||
Time="{Binding Time}"
|
||||
TimeFormat="{Binding TimeFormat}" />
|
||||
</DataTemplate>
|
||||
</u:Timeline.ItemTemplate>
|
||||
</u:Timeline>
|
||||
</UserControl>
|
||||
15
demo/Ursa.Demo/Pages/TimelineDemo.axaml.cs
Normal file
15
demo/Ursa.Demo/Pages/TimelineDemo.axaml.cs
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
110
demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs
Normal file
110
demo/Ursa.Demo/ViewModels/TimelineDemoViewModel.cs
Normal file
@@ -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; }
|
||||
}
|
||||
@@ -35,6 +35,9 @@
|
||||
<TabItem Header="IPv4Box">
|
||||
<pages:IPv4BoxDemo />
|
||||
</TabItem>
|
||||
<TabItem Header="Timeline">
|
||||
<pages:TimelineDemo />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
|
||||
|
||||
51
src/Ursa.Themes.Semi/Controls/Timeline.axaml
Normal file
51
src/Ursa.Themes.Semi/Controls/Timeline.axaml
Normal file
@@ -0,0 +1,51 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="https://irihi.tech/ursa">
|
||||
<Design.PreviewWith>
|
||||
<StackPanel Width="100" Spacing="20">
|
||||
<u:Timeline>
|
||||
<u:TimelineItem Content="Hello" Time="2022-01-01" />
|
||||
<u:TimelineItem Content="World" Time="2022-02-01" />
|
||||
<u:TimelineItem Content="!" Time="2022-03-01" />
|
||||
<u:TimelineItem />
|
||||
</u:Timeline>
|
||||
</StackPanel>
|
||||
</Design.PreviewWith>
|
||||
<!-- Add Resources Here -->
|
||||
<u:TimelineFormatConverter x:Key="FormatConverter" />
|
||||
<ControlTheme x:Key="{x:Type u:Timeline}" TargetType="u:Timeline">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:Timeline">
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:TimelineItem}" TargetType="u:TimelineItem">
|
||||
<Setter Property="u:TimelineItem.Template">
|
||||
<ControlTemplate TargetType="u:TimelineItem">
|
||||
<StackPanel>
|
||||
<ContentPresenter>
|
||||
<ContentPresenter.Content>
|
||||
<MultiBinding Converter="{StaticResource FormatConverter}">
|
||||
<Binding Path="Time" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
<Binding Path="TimeFormat" RelativeSource="{RelativeSource TemplatedParent}" />
|
||||
</MultiBinding>
|
||||
</ContentPresenter.Content>
|
||||
</ContentPresenter>
|
||||
<ContentPresenter Name="content" Content="{TemplateBinding Content}" />
|
||||
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:first /template/ ContentPresenter#content">
|
||||
<Setter Property="ContentPresenter.Foreground" Value="Red" />
|
||||
</Style>
|
||||
<Style Selector="^:last /template/ ContentPresenter#content">
|
||||
<Setter Property="ContentPresenter.Foreground" Value="Green" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
@@ -5,5 +5,6 @@
|
||||
<ResourceInclude Source="Banner.axaml" />
|
||||
<ResourceInclude Source="Divider.axaml" />
|
||||
<ResourceInclude Source="IPv4Box.axaml" />
|
||||
<ResourceInclude Source="Timeline.axaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/Ursa/Controls/Timeline/TimelineFormatConverter.cs
Normal file
17
src/Ursa/Controls/Timeline/TimelineFormatConverter.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public class TimelineFormatConverter: IMultiValueConverter
|
||||
{
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Count> 1 && values[0] is DateTime date && values[1] is string s)
|
||||
{
|
||||
return date.ToString(s, culture);
|
||||
}
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
@@ -12,7 +13,7 @@ public class TimelineItem: ContentControl
|
||||
{
|
||||
public const string PC_First = ":first";
|
||||
public const string PC_Last = ":last";
|
||||
|
||||
|
||||
public static readonly StyledProperty<IBrush> IconForegroundProperty =
|
||||
AvaloniaProperty.Register<TimelineItem, IBrush>(nameof(IconForeground));
|
||||
|
||||
@@ -40,7 +41,7 @@ public class TimelineItem: ContentControl
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string?> TimeFormatProperty = AvaloniaProperty.Register<TimelineItem, string?>(
|
||||
nameof(TimeFormat));
|
||||
nameof(TimeFormat), defaultValue:CultureInfo.CurrentUICulture.DateTimeFormat.SortableDateTimePattern);
|
||||
|
||||
public string? TimeFormat
|
||||
{
|
||||
@@ -56,4 +57,10 @@ public class TimelineItem: ContentControl
|
||||
get => GetValue(DescriptionTemplateProperty);
|
||||
set => SetValue(DescriptionTemplateProperty, value);
|
||||
}
|
||||
|
||||
internal void SetPosition(bool isFirst, bool isLast)
|
||||
{
|
||||
PseudoClasses.Set(PC_First, isFirst);
|
||||
PseudoClasses.Set(PC_Last, isLast);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Ursa.Controls;
|
||||
|
||||
public interface ITimelineItemData
|
||||
{
|
||||
public DateTime Time { get; set; }
|
||||
public object Content { get; set; }
|
||||
public object Description { get; set; }
|
||||
public TimelineItemType ItemType { get; set; }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Ursa.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Describe how TimelineItem components should be placed around the line.
|
||||
/// </summary>
|
||||
public enum TimelinePlacement
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
Alternate,
|
||||
}
|
||||
Reference in New Issue
Block a user