Fix a bunch of templating issues

This commit is contained in:
TheXamlGuy
2024-07-17 20:43:39 +01:00
parent 60b784aa25
commit efd00ff81a
11 changed files with 315 additions and 116 deletions
@@ -13,7 +13,44 @@
<x:Double x:Key="OverflowItemSpacing">6</x:Double>
<x:Double x:Key="OverflowItemSize">40</x:Double>
<CornerRadius x:Key="OverflowItemCornerRadius">40</CornerRadius>
<ControlTheme x:Key="OverflowItemStyle" TargetType="ListBoxItem">
<ControlTheme x:Key="{x:Type OverflowList}" TargetType="OverflowList">
<Setter Property="Foreground" Value="{DynamicResource ListBoxForeground}" />
<Setter Property="Background" Value="{DynamicResource ListBoxBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ListBoxBorder}" />
<Setter Property="BorderThickness" Value="{DynamicResource ListBoxBorderThemeThickness}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.IsScrollChainingEnabled" Value="True" />
<Setter Property="ScrollViewer.IsScrollInertiaEnabled" Value="True" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<ControlTemplate>
<Border
Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer
Name="PART_ScrollViewer"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
Background="{TemplateBinding Background}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
HorizontalSnapPointsType="{TemplateBinding (ScrollViewer.HorizontalSnapPointsType)}"
IsDeferredScrollingEnabled="{TemplateBinding (ScrollViewer.IsDeferredScrollingEnabled)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
IsScrollInertiaEnabled="{TemplateBinding (ScrollViewer.IsScrollInertiaEnabled)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
VerticalSnapPointsType="{TemplateBinding (ScrollViewer.VerticalSnapPointsType)}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Margin="{TemplateBinding Padding}"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type controls:OverflowItem}" TargetType="controls:OverflowItem">
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Background" Value="{DynamicResource ListViewItemBackground}" />
@@ -34,21 +71,28 @@
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
<Border
Name="SelectionIndicator"
BorderBrush="{DynamicResource AccentFillColorDefaultBrush}"
BorderThickness="3"
CornerRadius="{TemplateBinding CornerRadius}"
IsVisible="False"
UseLayoutRounding="False" />
</Grid>
<controls:ContentBadge
BadgePath="{TemplateBinding BadgePath}"
BadgePlacement="{TemplateBinding BadgePlacement}"
BadgeSize="{TemplateBinding BadgeSize}"
Foreground="Red"
IsBadgeVisible="{TemplateBinding IsBadgeVisible}">
<Grid>
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
/>
<Border
Name="SelectionIndicator"
BorderBrush="{DynamicResource AccentFillColorDefaultBrush}"
BorderThickness="3"
CornerRadius="{TemplateBinding CornerRadius}"
IsVisible="False"
UseLayoutRounding="False" />
</Grid>
</controls:ContentBadge>
</Border>
</Panel>
</ControlTemplate>
@@ -116,18 +160,17 @@
<Setter Property="Template">
<ControlTemplate>
<StackPanel Margin="{TemplateBinding Margin}">
<ListBox
<controls:OverflowList
x:Name="PrimaryListBox"
Grid.Column="0"
ItemContainerTheme="{StaticResource OverflowItemStyle}"
ItemTemplate="{TemplateBinding ItemTemplate}"
SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PrimarySelection, Mode=TwoWay}">
<ListBox.ItemsPanel>
<controls:OverflowList.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="{StaticResource OverflowItemSpacing}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</controls:OverflowList.ItemsPanel>
</controls:OverflowList>
<Grid
x:Name="Spacer"
Grid.Column="1"
@@ -153,17 +196,16 @@
</Viewbox>
<Button.Flyout>
<Flyout>
<ListBox
<controls:OverflowList
x:Name="SecondaryListBox"
ItemContainerTheme="{StaticResource OverflowItemStyle}"
ItemTemplate="{TemplateBinding ItemTemplate}"
SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.SecondarySelection, Mode=TwoWay}">
<ListBox.ItemsPanel>
<controls:OverflowList.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="{StaticResource OverflowItemSpacing}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</controls:OverflowList.ItemsPanel>
</controls:OverflowList>
</Flyout>
</Button.Flyout>
</Button>
@@ -29,13 +29,14 @@ public class Overflow :
private static readonly StyledProperty<OverflowTemplateSettings> TemplateSettingsProperty =
AvaloniaProperty.Register<Overflow, OverflowTemplateSettings>(nameof(TemplateSettings));
private readonly ObservableCollection<object> primaryCollection = [];
private readonly ObservableCollection<object> secondaryCollection = [];
private ListBox? primaryListBox;
private OverflowList? primaryListBox;
private ListBox? secondaryListBox;
private OverflowList? secondaryListBox;
public Overflow()
{
@@ -59,7 +60,6 @@ public class Overflow :
get => GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
[InheritDataTypeFromItems(nameof(ItemsSource))]
public IDataTemplate? ItemTemplate
{
@@ -83,10 +83,10 @@ public class Overflow :
{
base.OnApplyTemplate(args);
primaryListBox = args.NameScope.Get<ListBox>("PrimaryListBox");
primaryListBox = args.NameScope.Get<OverflowList>("PrimaryListBox");
primaryListBox?.SetValue(ItemsControl.ItemsSourceProperty, primaryCollection);
secondaryListBox = args.NameScope.Get<ListBox>("SecondaryListBox");
secondaryListBox = args.NameScope.Get<OverflowList>("SecondaryListBox");
secondaryListBox?.SetValue(ItemsControl.ItemsSourceProperty, secondaryCollection);
InitializeCollections();
@@ -0,0 +1,45 @@
using Avalonia;
using Avalonia.Controls;
namespace Toolkit.UI.Controls.Avalonia;
public class OverflowItem :
ListBoxItem
{
public static readonly StyledProperty<double> BadgeSizeProperty =
AvaloniaProperty.Register<OverflowItem, double>(nameof(BadgeSize), 14);
public static readonly StyledProperty<bool> IsBadgeVisibleProperty =
AvaloniaProperty.Register<OverflowItem, bool>(nameof(IsBadgeVisible), true);
public static readonly StyledProperty<string> BadgePathProperty =
AvaloniaProperty.Register<OverflowItem, string>(nameof(BadgePath));
public static readonly StyledProperty<ContentBadgePlacement> BadgePlacementProperty =
AvaloniaProperty.Register<OverflowItem, ContentBadgePlacement>(nameof(BadgePlacement), ContentBadgePlacement.BottomRight);
public string BadgePath
{
get => GetValue(BadgePathProperty);
set => SetValue(BadgePathProperty, value);
}
public ContentBadgePlacement BadgePlacement
{
get => GetValue(BadgePlacementProperty);
set => SetValue(BadgePlacementProperty, value);
}
public double BadgeSize
{
get => GetValue(BadgeSizeProperty);
set => SetValue(BadgeSizeProperty, value);
}
public bool IsBadgeVisible
{
get => GetValue(IsBadgeVisibleProperty);
set => SetValue(IsBadgeVisibleProperty, value);
}
}
@@ -0,0 +1,39 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
namespace Toolkit.UI.Controls.Avalonia;
public class OverflowList :
ListBox
{
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
if (recycleKey is IDataTemplate itemContainerTemplate)
{
if (itemContainerTemplate.Build(item) is OverflowItem container)
{
return container;
}
}
return new OverflowItem();
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
if (item is OverflowItem)
{
recycleKey = null;
return false;
}
if (this.FindDataTemplate(item, ItemTemplate) is IDataTemplate itemContainerTemplate)
{
recycleKey = itemContainerTemplate;
return true;
}
recycleKey = DefaultRecycleKey;
return true;
}
}
@@ -0,0 +1,43 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
namespace Toolkit.UI.Controls.Avalonia;
public class TemplateListBox :
ListBox
{
protected override Type StyleKeyOverride =>
typeof(ListBox);
protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
{
if (recycleKey is IDataTemplate itemContainerTemplate)
{
if (itemContainerTemplate.Build(item) is ListBoxItem container)
{
return container;
}
}
return new ListBoxItem();
}
protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
if (item is ListBoxItem)
{
recycleKey = null;
return false;
}
if (this.FindDataTemplate(item, ItemTemplate) is IDataTemplate itemContainerTemplate)
{
recycleKey = itemContainerTemplate;
return true;
}
recycleKey = DefaultRecycleKey;
return true;
}
}