feat: start to refactor.
This commit is contained in:
@@ -144,6 +144,7 @@
|
|||||||
IPAddress="{Binding Address}" />
|
IPAddress="{Binding Address}" />
|
||||||
<u:IPv4Box HorizontalAlignment="Stretch" IsEnabled="False" />
|
<u:IPv4Box HorizontalAlignment="Stretch" IsEnabled="False" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<!--
|
||||||
<u:Timeline Grid.Column="1">
|
<u:Timeline Grid.Column="1">
|
||||||
<u:TimelineItem
|
<u:TimelineItem
|
||||||
Content="ToDo"
|
Content="ToDo"
|
||||||
@@ -166,6 +167,7 @@
|
|||||||
ItemType="Error"
|
ItemType="Error"
|
||||||
Time="2022-01-05" />
|
Time="2022-01-05" />
|
||||||
</u:Timeline>
|
</u:Timeline>
|
||||||
|
-->
|
||||||
<StackPanel Grid.Column="2" Spacing="20">
|
<StackPanel Grid.Column="2" Spacing="20">
|
||||||
<u:ButtonGroup Classes="Primary Solid" ItemsSource="{Binding ButtonGroupItems}" />
|
<u:ButtonGroup Classes="Primary Solid" ItemsSource="{Binding ButtonGroupItems}" />
|
||||||
<u:ButtonGroup Classes="Primary" ItemsSource="{Binding ButtonGroupItems}" />
|
<u:ButtonGroup Classes="Primary" ItemsSource="{Binding ButtonGroupItems}" />
|
||||||
|
|||||||
@@ -8,51 +8,19 @@
|
|||||||
xmlns:viewModels="clr-namespace:Ursa.Demo.ViewModels"
|
xmlns:viewModels="clr-namespace:Ursa.Demo.ViewModels"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:CompileBindings="False"
|
|
||||||
x:DataType="viewModels:TimelineDemoViewModel"
|
x:DataType="viewModels:TimelineDemoViewModel"
|
||||||
|
x:CompileBindings="True"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
|
||||||
<u:TimelineFormatConverter x:Key="FormatConverter" />
|
|
||||||
</UserControl.Resources>
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<u:Timeline>
|
<u:Timeline ItemsSource="{Binding Items}"
|
||||||
<u:TimelineItem
|
HeaderMemberBinding="{ReflectionBinding Header}"
|
||||||
Content="Start"
|
DescriptionMemberBinding="{ReflectionBinding Description}"
|
||||||
ItemType="Warning"
|
>
|
||||||
Time="2022-01-01" />
|
|
||||||
<u:TimelineItem
|
|
||||||
Content="In between"
|
|
||||||
ItemType="Ongoing"
|
|
||||||
Time="2022-01-02" />
|
|
||||||
<u:TimelineItem
|
|
||||||
Content="Finished"
|
|
||||||
ItemType="Error"
|
|
||||||
Time="2022-01-03" />
|
|
||||||
<u:TimelineItem
|
|
||||||
Content="Finished"
|
|
||||||
IconForeground="Yellow"
|
|
||||||
ItemType="Default"
|
|
||||||
Time="2022-01-03" />
|
|
||||||
</u:Timeline>
|
</u:Timeline>
|
||||||
<u:Timeline HorizontalAlignment="Left" ItemsSource="{Binding Items}">
|
<u:Timeline>
|
||||||
<u:Timeline.ItemTemplate>
|
<u:TimelineItem Header="第一步" Content="Step 1"></u:TimelineItem>
|
||||||
<DataTemplate x:DataType="viewModels:TimelineItemViewModel">
|
<u:TimelineItem Header="第二步" Content="Step 2"></u:TimelineItem>
|
||||||
<u:TimelineItem
|
<u:TimelineItem Header="第三步" Content="Step 3"></u:TimelineItem>
|
||||||
Content="{Binding Content}"
|
|
||||||
ItemType="{Binding ItemType}"
|
|
||||||
Time="{Binding Time}"
|
|
||||||
TimeFormat="{Binding TimeFormat}">
|
|
||||||
<u:TimelineItem.ContentTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock
|
|
||||||
MaxWidth="100"
|
|
||||||
Text="{Binding}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
</DataTemplate>
|
|
||||||
</u:TimelineItem.ContentTemplate>
|
|
||||||
</u:TimelineItem>
|
|
||||||
</DataTemplate>
|
|
||||||
</u:Timeline.ItemTemplate>
|
|
||||||
</u:Timeline>
|
</u:Timeline>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class TimelineDemoViewModel: ViewModelBase
|
|||||||
Time = DateTime.Now,
|
Time = DateTime.Now,
|
||||||
TimeFormat = "yyyy-MM-dd HH:mm:ss",
|
TimeFormat = "yyyy-MM-dd HH:mm:ss",
|
||||||
Description = "Item 1",
|
Description = "Item 1",
|
||||||
Content = "First",
|
Header = "审核中",
|
||||||
ItemType = TimelineItemType.Success,
|
ItemType = TimelineItemType.Success,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
@@ -21,7 +21,7 @@ public class TimelineDemoViewModel: ViewModelBase
|
|||||||
Time = DateTime.Now,
|
Time = DateTime.Now,
|
||||||
TimeFormat = "HH:mm:ss",
|
TimeFormat = "HH:mm:ss",
|
||||||
Description = "Item 2",
|
Description = "Item 2",
|
||||||
Content = "Content 2",
|
Header = "发布成功",
|
||||||
ItemType = TimelineItemType.Success,
|
ItemType = TimelineItemType.Success,
|
||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
@@ -29,23 +29,9 @@ public class TimelineDemoViewModel: ViewModelBase
|
|||||||
Time = DateTime.Now,
|
Time = DateTime.Now,
|
||||||
TimeFormat = "HH:mm:ss",
|
TimeFormat = "HH:mm:ss",
|
||||||
Description = "Item 3",
|
Description = "Item 3",
|
||||||
Content = "Content 3",
|
Header = "审核失败",
|
||||||
ItemType = TimelineItemType.Ongoing,
|
ItemType = TimelineItemType.Ongoing,
|
||||||
},
|
}
|
||||||
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"
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +40,6 @@ public class TimelineItemViewModel: ObservableObject
|
|||||||
public DateTime Time { get; set; }
|
public DateTime Time { get; set; }
|
||||||
public string? TimeFormat { get; set; }
|
public string? TimeFormat { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public string? Content { get; set; }
|
public string? Header { get; set; }
|
||||||
public TimelineItemType ItemType { get; set; }
|
public TimelineItemType ItemType { get; set; }
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,9 @@
|
|||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<StackPanel Width="100" Spacing="20">
|
<StackPanel Width="100" Spacing="20">
|
||||||
<u:Timeline>
|
<u:Timeline>
|
||||||
<u:TimelineItem Content="Hello" Time="2022-01-01" />
|
<u:TimelineItem Content="Hello" />
|
||||||
<u:TimelineItem Content="World" Time="2022-02-01" />
|
<u:TimelineItem Content="World" />
|
||||||
<u:TimelineItem Content="!" Time="2022-03-01" />
|
<u:TimelineItem Content="!" />
|
||||||
<u:TimelineItem />
|
<u:TimelineItem />
|
||||||
</u:Timeline>
|
</u:Timeline>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -81,13 +81,9 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
|
Content="{TemplateBinding Header}"
|
||||||
|
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||||
Foreground="Gray">
|
Foreground="Gray">
|
||||||
<ContentPresenter.Content>
|
|
||||||
<MultiBinding Converter="{StaticResource FormatConverter}">
|
|
||||||
<Binding Path="Time" RelativeSource="{RelativeSource TemplatedParent}" />
|
|
||||||
<Binding Path="TimeFormat" RelativeSource="{RelativeSource TemplatedParent}" />
|
|
||||||
</MultiBinding>
|
|
||||||
</ContentPresenter.Content>
|
|
||||||
</ContentPresenter>
|
</ContentPresenter>
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
Name="content"
|
Name="content"
|
||||||
|
|||||||
@@ -1,56 +1,104 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Generators;
|
using Avalonia.Controls.Generators;
|
||||||
using Avalonia.Controls.Presenters;
|
using Avalonia.Controls.Presenters;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
public class Timeline: ItemsControl
|
public class Timeline: ItemsControl
|
||||||
{
|
{
|
||||||
|
public static readonly StyledProperty<IBinding?> IconMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
|
||||||
|
nameof(IconMemberBinding));
|
||||||
|
|
||||||
public static readonly StyledProperty<IDataTemplate?> ItemDescriptionTemplateProperty = AvaloniaProperty.Register<Timeline, IDataTemplate?>(
|
[AssignBinding]
|
||||||
nameof(ItemDescriptionTemplate));
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? IconMemberBinding
|
||||||
public IDataTemplate? ItemDescriptionTemplate
|
|
||||||
{
|
{
|
||||||
get => GetValue(ItemDescriptionTemplateProperty);
|
get => GetValue(IconMemberBindingProperty);
|
||||||
set => SetValue(ItemDescriptionTemplateProperty, value);
|
set => SetValue(IconMemberBindingProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Timeline()
|
public static readonly StyledProperty<IBinding?> HeaderMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
|
||||||
|
nameof(HeaderMemberBinding));
|
||||||
|
|
||||||
|
[AssignBinding]
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? HeaderMemberBinding
|
||||||
{
|
{
|
||||||
ItemsView.CollectionChanged+=ItemsViewOnCollectionChanged;
|
get => GetValue(HeaderMemberBindingProperty);
|
||||||
|
set => SetValue(HeaderMemberBindingProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ItemsViewOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
public static readonly StyledProperty<IBinding?> DescriptionMemberBindingProperty = AvaloniaProperty.Register<Timeline, IBinding?>(
|
||||||
|
nameof(DescriptionMemberBinding));
|
||||||
|
|
||||||
|
[AssignBinding]
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? DescriptionMemberBinding
|
||||||
{
|
{
|
||||||
RefreshTimelineItems();
|
get => GetValue(DescriptionMemberBindingProperty);
|
||||||
|
set => SetValue(DescriptionMemberBindingProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
||||||
|
public static readonly StyledProperty<IDataTemplate?> IconTemplateProperty = AvaloniaProperty.Register<Timeline, IDataTemplate?>(
|
||||||
|
nameof(IconTemplate));
|
||||||
|
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IDataTemplate? IconTemplate
|
||||||
{
|
{
|
||||||
base.OnPropertyChanged(change);
|
get => GetValue(IconTemplateProperty);
|
||||||
RefreshTimelineItems();
|
set => SetValue(IconTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshTimelineItems()
|
public static readonly StyledProperty<IDataTemplate?> DescriptionTemplateProperty = AvaloniaProperty.Register<Timeline, IDataTemplate?>(
|
||||||
|
nameof(DescriptionTemplate));
|
||||||
|
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IDataTemplate? DescriptionTemplate
|
||||||
{
|
{
|
||||||
for (int i = 0; i < this.LogicalChildren.Count; i++)
|
get => GetValue(DescriptionTemplateProperty);
|
||||||
|
set => SetValue(DescriptionTemplateProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
if (this.LogicalChildren[i] is TimelineItem t)
|
if (IconMemberBinding != null)
|
||||||
{
|
{
|
||||||
t.SetIndex(i == 0, i == this.LogicalChildren.Count - 1);
|
t.Bind(TimelineItem.IconProperty, IconMemberBinding);
|
||||||
}
|
}
|
||||||
else if (this.LogicalChildren[i] is ContentPresenter { Child: TimelineItem t2 })
|
if (HeaderMemberBinding != null)
|
||||||
{
|
{
|
||||||
t2.SetIndex(i == 0, i == this.LogicalChildren.Count - 1);
|
t.Bind(HeaderedContentControl.HeaderProperty, HeaderMemberBinding);
|
||||||
}
|
}
|
||||||
|
if (DescriptionMemberBinding != null)
|
||||||
|
{
|
||||||
|
t.Bind(ContentControl.ContentProperty, DescriptionMemberBinding);
|
||||||
|
}
|
||||||
|
t.SetCurrentValue(TimelineItem.IconTemplateProperty, IconTemplate);
|
||||||
|
t.SetCurrentValue(HeaderedContentControl.HeaderTemplateProperty, ItemTemplate);
|
||||||
|
t.SetCurrentValue(ContentControl.ContentTemplateProperty, DescriptionTemplate);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,101 +1,41 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Data;
|
using Avalonia.Data;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
[PseudoClasses(PC_First, PC_Last, PC_Default, PC_Ongoing, PC_Success, PC_Warning, PC_Error, PC_None)]
|
public class TimelineItem: HeaderedContentControl
|
||||||
public class TimelineItem: ContentControl
|
|
||||||
{
|
{
|
||||||
private const string PC_First = ":first";
|
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<TimelineItem, object?>(
|
||||||
private const string PC_Last = ":last";
|
nameof(Icon));
|
||||||
private const string PC_Default = ":default";
|
|
||||||
private const string PC_Ongoing = ":ongoing";
|
|
||||||
private const string PC_Success = ":success";
|
|
||||||
private const string PC_Warning = ":warning";
|
|
||||||
private const string PC_Error = ":error";
|
|
||||||
private const string PC_None = ":none";
|
|
||||||
|
|
||||||
private static readonly IReadOnlyDictionary<TimelineItemType, string> _itemTypeMapping = new Dictionary<TimelineItemType, string>
|
public object? Icon
|
||||||
{
|
{
|
||||||
{TimelineItemType.Default, PC_Default},
|
get => GetValue(IconProperty);
|
||||||
{TimelineItemType.Ongoing, PC_Ongoing},
|
set => SetValue(IconProperty, value);
|
||||||
{TimelineItemType.Success, PC_Success},
|
|
||||||
{TimelineItemType.Warning, PC_Warning},
|
|
||||||
{TimelineItemType.Error, PC_Error},
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly StyledProperty<IBrush> IconForegroundProperty =
|
|
||||||
AvaloniaProperty.Register<TimelineItem, IBrush>(nameof(IconForeground));
|
|
||||||
|
|
||||||
public IBrush IconForeground
|
|
||||||
{
|
|
||||||
get => GetValue(IconForegroundProperty);
|
|
||||||
set => SetValue(IconForegroundProperty, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<DateTime> TimeProperty = AvaloniaProperty.Register<TimelineItem, DateTime>(
|
public static readonly StyledProperty<IDataTemplate?> IconTemplateProperty = AvaloniaProperty.Register<TimelineItem, IDataTemplate?>(
|
||||||
nameof(Time));
|
nameof(IconTemplate));
|
||||||
public DateTime Time
|
|
||||||
|
public IDataTemplate? IconTemplate
|
||||||
{
|
{
|
||||||
get => GetValue(TimeProperty);
|
get => GetValue(IconTemplateProperty);
|
||||||
set => SetValue(TimeProperty, value);
|
set => SetValue(IconTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<string?> TimeFormatProperty = AvaloniaProperty.Register<TimelineItem, string?>(
|
public static readonly StyledProperty<TimelineItemType> TypeProperty = AvaloniaProperty.Register<TimelineItem, TimelineItemType>(
|
||||||
nameof(TimeFormat), defaultValue:CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern);
|
nameof(Type));
|
||||||
|
|
||||||
public string? TimeFormat
|
public TimelineItemType Type
|
||||||
{
|
{
|
||||||
get => GetValue(TimeFormatProperty);
|
get => GetValue(TypeProperty);
|
||||||
set => SetValue(TimeFormatProperty, value);
|
set => SetValue(TypeProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<IDataTemplate> DescriptionTemplateProperty = AvaloniaProperty.Register<TimelineItem, IDataTemplate>(
|
|
||||||
nameof(DescriptionTemplate));
|
|
||||||
|
|
||||||
public IDataTemplate DescriptionTemplate
|
|
||||||
{
|
|
||||||
get => GetValue(DescriptionTemplateProperty);
|
|
||||||
set => SetValue(DescriptionTemplateProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly StyledProperty<TimelineItemType> ItemTypeProperty = AvaloniaProperty.Register<TimelineItem, TimelineItemType>(
|
|
||||||
nameof(ItemType));
|
|
||||||
|
|
||||||
public TimelineItemType ItemType
|
|
||||||
{
|
|
||||||
get => GetValue(ItemTypeProperty);
|
|
||||||
set => SetValue(ItemTypeProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetIndex(bool isFirst, bool isLast)
|
|
||||||
{
|
|
||||||
PseudoClasses.Set(PC_First, isFirst);
|
|
||||||
PseudoClasses.Set(PC_Last, isLast);
|
|
||||||
}
|
|
||||||
|
|
||||||
static TimelineItem()
|
|
||||||
{
|
|
||||||
ItemTypeProperty.Changed.AddClassHandler<TimelineItem>((o, e) => { o.OnItemTypeChanged(e); });
|
|
||||||
IconForegroundProperty.Changed.AddClassHandler<TimelineItem>((o, e) => { o.OnIconForegroundChanged(e); });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnItemTypeChanged(AvaloniaPropertyChangedEventArgs args)
|
|
||||||
{
|
|
||||||
var oldValue = args.GetOldValue<TimelineItemType>();
|
|
||||||
var newValue = args.GetNewValue<TimelineItemType>();
|
|
||||||
PseudoClasses.Set(_itemTypeMapping[oldValue], false);
|
|
||||||
PseudoClasses.Set(_itemTypeMapping[newValue], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnIconForegroundChanged(AvaloniaPropertyChangedEventArgs args)
|
|
||||||
{
|
|
||||||
IBrush? newValue = args.GetOldValue<IBrush?>();
|
|
||||||
PseudoClasses.Set(PC_None, newValue is null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user