feat: Add ElementGroup control, styles and demo.
This commit is contained in:
1 parent
700abba273
commit
2ac7dfaf96
6 files changed
+289
No files matched your search
@@ -0,0 +1,45 @@
|
|||||||
|
<UserControl x:Class="HandyControlDemo.UserControl.ElementGroupDemo"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
|
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
|
||||||
|
Background="{DynamicResource RegionBrush}">
|
||||||
|
<TransitioningContentControl>
|
||||||
|
<hc:UniformSpacingPanel Margin="32" VerticalAlignment="Center" Orientation="Vertical" Spacing="16">
|
||||||
|
<hc:ElementGroup Orientation="Horizontal" Layout="Stack">
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
</hc:ElementGroup>
|
||||||
|
<hc:ElementGroup Orientation="Horizontal" MaxWidth="500">
|
||||||
|
<ToggleButton HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
<ToggleButton HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||||
|
<Button HorizontalAlignment="Stretch" Width="NaN" Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
</hc:ElementGroup>
|
||||||
|
<hc:ElementGroup Orientation="Horizontal" Layout="Stack">
|
||||||
|
<Border Theme="{StaticResource BorderRegion}" Padding="6,0">
|
||||||
|
<Path Data="{StaticResource ClockGeometry}" Width="16" Height="16" Stretch="Uniform" Fill="{DynamicResource BorderBrush}"/>
|
||||||
|
</Border>
|
||||||
|
<TextBox MinWidth="200"/>
|
||||||
|
<Button Content="{x:Static langs:Lang.Button}"/>
|
||||||
|
<ToggleButton Content="{x:Static langs:Lang.ToggleButton}"/>
|
||||||
|
</hc:ElementGroup>
|
||||||
|
<hc:ElementGroup>
|
||||||
|
<hc:ElementGroup.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</hc:ElementGroup.ItemsPanel>
|
||||||
|
<TextBox HorizontalAlignment="Stretch"/>
|
||||||
|
<Button Grid.Column="1" Width="100" Content="{x:Static langs:Lang.Button}" HorizontalAlignment="Right" Theme="{StaticResource ButtonPrimary}"/>
|
||||||
|
</hc:ElementGroup>
|
||||||
|
</hc:UniformSpacingPanel>
|
||||||
|
</TransitioningContentControl>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace HandyControlDemo.UserControl;
|
||||||
|
|
||||||
|
public partial class ElementGroupDemo : Avalonia.Controls.UserControl
|
||||||
|
{
|
||||||
|
public ElementGroupDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using HandyControl.Data;
|
||||||
|
using HandyControl.Tools;
|
||||||
|
|
||||||
|
namespace HandyControl.Controls;
|
||||||
|
|
||||||
|
public class ElementGroup : ItemsControl
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>> CornerRadiusDict;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Orientation> OrientationProperty =
|
||||||
|
StackPanel.OrientationProperty.AddOwner<ElementGroup>(
|
||||||
|
new StyledPropertyMetadata<Orientation>(defaultValue: Orientation.Horizontal));
|
||||||
|
|
||||||
|
public Orientation Orientation
|
||||||
|
{
|
||||||
|
get => GetValue(OrientationProperty);
|
||||||
|
set => SetValue(OrientationProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<LinearLayout> LayoutProperty =
|
||||||
|
AvaloniaProperty.Register<ElementGroup, LinearLayout>(nameof(Layout), defaultValue: LinearLayout.Uniform);
|
||||||
|
|
||||||
|
public LinearLayout Layout
|
||||||
|
{
|
||||||
|
get => GetValue(LayoutProperty);
|
||||||
|
set => SetValue(LayoutProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ElementGroup()
|
||||||
|
{
|
||||||
|
AffectsMeasure<ElementGroup>(OrientationProperty, LayoutProperty);
|
||||||
|
|
||||||
|
var defaultCornerRadius = ResourceHelper.GetResource<CornerRadius>("DefaultCornerRadius");
|
||||||
|
|
||||||
|
CornerRadiusDict = new Dictionary<Orientation, Dictionary<ChildLocation, CornerRadius>>
|
||||||
|
{
|
||||||
|
[Orientation.Horizontal] = new()
|
||||||
|
{
|
||||||
|
[ChildLocation.Single] = defaultCornerRadius,
|
||||||
|
[ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, 0, 0, defaultCornerRadius.BottomLeft),
|
||||||
|
[ChildLocation.Middle] = new CornerRadius(),
|
||||||
|
[ChildLocation.Last] = new CornerRadius(0, defaultCornerRadius.TopRight, defaultCornerRadius.BottomRight, 0)
|
||||||
|
},
|
||||||
|
[Orientation.Vertical] = new()
|
||||||
|
{
|
||||||
|
[ChildLocation.Single] = defaultCornerRadius,
|
||||||
|
[ChildLocation.First] = new CornerRadius(defaultCornerRadius.TopLeft, defaultCornerRadius.TopRight, 0, 0),
|
||||||
|
[ChildLocation.Middle] = new CornerRadius(),
|
||||||
|
[ChildLocation.Last] = new CornerRadius(0, 0, defaultCornerRadius.BottomRight, defaultCornerRadius.BottomLeft)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.PrepareContainerForItemOverride(container, item, index);
|
||||||
|
|
||||||
|
container.GotFocus += ElementAdded_GotFocus;
|
||||||
|
container.LostFocus += ElementAdded_LostFocus;
|
||||||
|
container.PropertyChanged += Container_PropertyChanged;
|
||||||
|
|
||||||
|
UpdateChildrenCornerRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ClearContainerForItemOverride(Control container)
|
||||||
|
{
|
||||||
|
base.ClearContainerForItemOverride(container);
|
||||||
|
|
||||||
|
container.GotFocus -= ElementAdded_GotFocus;
|
||||||
|
container.LostFocus -= ElementAdded_LostFocus;
|
||||||
|
container.PropertyChanged -= Container_PropertyChanged;
|
||||||
|
|
||||||
|
UpdateChildrenCornerRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Container_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Property == IsVisibleProperty)
|
||||||
|
{
|
||||||
|
UpdateChildrenCornerRadius();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateChildrenCornerRadius()
|
||||||
|
{
|
||||||
|
var visibleChildren = GetVisibleChildren();
|
||||||
|
var count = visibleChildren.Count;
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orientation = Orientation;
|
||||||
|
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.Single]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateChildCornerRadius(visibleChildren[0], CornerRadiusDict[orientation][ChildLocation.First]);
|
||||||
|
|
||||||
|
var childMargin = orientation == Orientation.Horizontal
|
||||||
|
? new Thickness(-1, 0, 0, 0)
|
||||||
|
: new Thickness(0, -1, 0, 0);
|
||||||
|
|
||||||
|
for (var childIndex = 1; childIndex < count; childIndex++)
|
||||||
|
{
|
||||||
|
var child = visibleChildren[childIndex];
|
||||||
|
child.SetCurrentValue(MarginProperty, childMargin);
|
||||||
|
UpdateChildCornerRadius(child, CornerRadiusDict[orientation][ChildLocation.Middle]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastChild = visibleChildren[count - 1];
|
||||||
|
lastChild.SetCurrentValue(MarginProperty, childMargin);
|
||||||
|
UpdateChildCornerRadius(lastChild, CornerRadiusDict[orientation][ChildLocation.Last]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Control> GetVisibleChildren()
|
||||||
|
{
|
||||||
|
return Items.OfType<Control>().Where(element => element.IsVisible).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateChildCornerRadius(AvaloniaObject child, CornerRadius cornerRadius)
|
||||||
|
{
|
||||||
|
if (child is Border border)
|
||||||
|
{
|
||||||
|
border.SetCurrentValue(Border.CornerRadiusProperty, cornerRadius);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BorderElement.SetCornerRadius(child, cornerRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ElementAdded_LostFocus(object? sender, RoutedEventArgs e) => ResetElementZIndex(e.Source);
|
||||||
|
|
||||||
|
private void ElementAdded_GotFocus(object? sender, RoutedEventArgs e) => MaximizeElementZIndex(e.Source);
|
||||||
|
|
||||||
|
private static void ResetElementZIndex(object? element)
|
||||||
|
{
|
||||||
|
if (element is Control control)
|
||||||
|
{
|
||||||
|
control.ZIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MaximizeElementZIndex(object? element)
|
||||||
|
{
|
||||||
|
if (element is Control control)
|
||||||
|
{
|
||||||
|
control.ZIndex = int.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ChildLocation
|
||||||
|
{
|
||||||
|
Single,
|
||||||
|
First,
|
||||||
|
Middle,
|
||||||
|
Last
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace HandyControl.Data;
|
||||||
|
|
||||||
|
public enum LinearLayout
|
||||||
|
{
|
||||||
|
Stack,
|
||||||
|
|
||||||
|
Uniform
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:ClassModifier="internal"
|
||||||
|
xmlns:hc="clr-namespace:HandyControl.Controls">
|
||||||
|
|
||||||
|
<ItemsPanelTemplate x:Key="UniformHorizontalItemsPanelTemplate">
|
||||||
|
<UniformGrid Rows="1" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
|
||||||
|
<ItemsPanelTemplate x:Key="UniformVerticalItemsPanelTemplate">
|
||||||
|
<UniformGrid Columns="1" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
|
||||||
|
<ItemsPanelTemplate x:Key="StackHorizontalItemsPanelTemplate">
|
||||||
|
<StackPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
|
||||||
|
<ItemsPanelTemplate x:Key="StackVerticalItemsPanelTemplate">
|
||||||
|
<StackPanel Orientation="Vertical" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="ElementGroupBaseStyle"
|
||||||
|
TargetType="hc:ElementGroup">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top" />
|
||||||
|
<Setter Property="Layout" Value="Uniform" />
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="ItemsPanel" Value="{StaticResource UniformHorizontalItemsPanelTemplate}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="hc:ElementGroup">
|
||||||
|
<Border Padding="{TemplateBinding Padding}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<ItemsPresenter Name="PART_ItemsPresenter"
|
||||||
|
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^[Layout=Uniform][Orientation=Vertical]">
|
||||||
|
<Setter Property="ItemsPanel" Value="{StaticResource UniformVerticalItemsPanelTemplate}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Layout=Stack][Orientation=Horizontal]">
|
||||||
|
<Setter Property="ItemsPanel" Value="{StaticResource StackHorizontalItemsPanelTemplate}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Layout=Stack][Orientation=Vertical]">
|
||||||
|
<Setter Property="ItemsPanel" Value="{StaticResource StackVerticalItemsPanelTemplate}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type hc:ElementGroup}"
|
||||||
|
BasedOn="{StaticResource ElementGroupBaseStyle}"
|
||||||
|
TargetType="hc:ElementGroup" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -58,6 +58,7 @@
|
|||||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/SelectableTextBlock.axaml" />
|
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/SelectableTextBlock.axaml" />
|
||||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/HeaderedContentControl.axaml" />
|
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/HeaderedContentControl.axaml" />
|
||||||
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Card.axaml" />
|
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/Card.axaml" />
|
||||||
|
<MergeResourceInclude Source="avares://HandyControl/Themes/Styles/ElementGroup.axaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
|
|||||||
Reference in new issue
Block a user