feat: add avalonia menu style (step1).

This commit is contained in:
NaBian committed 2025-08-28 22:59:44 +08:00
1 parent aa4fa95ab1
commit bb3c075398
11 files changed
+400 -11

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<AvaloniaVersion>11.3.2</AvaloniaVersion>
<AvaloniaVersion>11.3.4</AvaloniaVersion>
</PropertyGroup>
</Project>
@@ -19,19 +19,12 @@
[ "Expander", "ExpanderDemo", "Brush.Expander", "", "" ],
[ "ProgressBar", "NativeProgressBarDemo", "Brush.ProgressBar", "", "" ],
[ "Calendar", "CalendarDemo", "Brush.Calendar", "", "" ],
[ "DatePicker", "NativeDatePickerDemo", "Brush.DateTimePicker", "", "" ],
[ "TabControl", "NativeTabControlDemo", "Brush.TabPage", "", "" ],
[ "DataGrid", "DataGridDemo", "Brush.DataGrid", "", "" ],
[ "TreeView", "TreeViewDemo", "Brush.TreeView", "", "" ],
[ "ListBox", "ListBoxDemo", "Brush.ListBox", "", "" ],
[ "GroupBox", "GroupBoxDemo", "Brush.GroupBox", "", "" ],
[ "Menu", "MenuDemo", "Brush.ContextMenu", "", "" ],
[ "ToolBar", "ToolBarDemo", "Brush.ToolBar", "", "" ],
[ "Border", "BorderDemo", "Brush.BorderElement", "", "" ],
[ "Label", "LabelDemo", "Brush.Label", "", "" ],
[ "Frame", "FrameDemo", "Brush.FrameBorder", "", "" ],
[ "Window", "NativeWindowDemo", "Brush.WindowsForm", "", "" ],
[ "Geometry", "GeometryDemo", "Brush.Path", "", "" ]
]
},
{
@@ -0,0 +1,57 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
x:Class="HandyControlDemo.UserControl.MenuDemo">
<hc:UniformSpacingPanel Spacing="16"
Margin="32">
<hc:DashedBorder Width="200"
Height="112"
Background="Transparent"
CornerRadius="4"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
BorderDashArray="2,2">
<hc:DashedBorder.ContextMenu>
<ContextMenu ItemsSource="{Binding DataList}">
<ContextMenu.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding DataList}">
<TextBlock Text="{Binding Name}" />
</TreeDataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</hc:DashedBorder.ContextMenu>
<TextBlock Foreground="{DynamicResource BorderBrush}"
Theme="{StaticResource TextBlockTitle}"
TextAlignment="Center">
<Run Text="Right click here" />
<LineBreak />
<Run Text="Default" />
</TextBlock>
</hc:DashedBorder>
<hc:DashedBorder Width="200"
Height="112"
Background="Transparent"
CornerRadius="4"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
BorderDashArray="2,2">
<hc:DashedBorder.ContextMenu>
<ContextMenu ItemsSource="{Binding DataList}"
Theme="{StaticResource ContextMenu.Small}">
<ContextMenu.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding DataList}">
<TextBlock Text="{Binding Name}" />
</TreeDataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</hc:DashedBorder.ContextMenu>
<TextBlock Foreground="{DynamicResource BorderBrush}"
Theme="{StaticResource TextBlockTitle}"
TextAlignment="Center">
<Run Text="Right click here" />
<LineBreak />
<Run Text="Small" />
</TextBlock>
</hc:DashedBorder>
</hc:UniformSpacingPanel>
</UserControl>
@@ -0,0 +1,9 @@
namespace HandyControlDemo.UserControl;
public partial class MenuDemo : Avalonia.Controls.UserControl
{
public MenuDemo()
{
InitializeComponent();
}
}
@@ -0,0 +1,32 @@
using Avalonia;
namespace HandyControl.Controls;
public class MenuAttach
{
public static readonly AttachedProperty<double> PopupVerticalOffsetProperty =
AvaloniaProperty.RegisterAttached<MenuAttach, AvaloniaObject, double>("PopupVerticalOffset");
public static void SetPopupVerticalOffset(AvaloniaObject element, double value) =>
element.SetValue(PopupVerticalOffsetProperty, value);
public static double GetPopupVerticalOffset(AvaloniaObject element) =>
element.GetValue(PopupVerticalOffsetProperty);
public static readonly AttachedProperty<double> PopupHorizontalOffsetProperty =
AvaloniaProperty.RegisterAttached<MenuAttach, AvaloniaObject, double>("PopupHorizontalOffset");
public static void SetPopupHorizontalOffset(AvaloniaObject element, double value) =>
element.SetValue(PopupHorizontalOffsetProperty, value);
public static double GetPopupHorizontalOffset(AvaloniaObject element) =>
element.GetValue(PopupHorizontalOffsetProperty);
public static readonly AttachedProperty<Thickness> ItemPaddingProperty =
AvaloniaProperty.RegisterAttached<MenuAttach, AvaloniaObject, Thickness>("ItemPadding");
public static void SetItemPadding(AvaloniaObject element, Thickness value) =>
element.SetValue(ItemPaddingProperty, value);
public static Thickness GetItemPadding(AvaloniaObject element) => element.GetValue(ItemPaddingProperty);
}
@@ -12,6 +12,7 @@
<hc:Double2GridLengthConverter x:Key="Double2GridLengthConverter" />
<hc:ThicknessSplitConverter x:Key="ThicknessSplitConverter" />
<avalonia:StringFormatConverter x:Key="StringFormatConverter" />
<avalonia:PlatformKeyGestureConverter x:Key="PlatformKeyGestureConverter" />
<avalonia:MarginMultiplierConverter x:Key="LeftMarginConverter"
Indent="16"
Left="True" />
@@ -0,0 +1,58 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol">
<ControlTheme x:Key="ContextMenuBaseStyle"
TargetType="ContextMenu">
<Setter Property="hc:MenuAttach.ItemPadding"
Value="{StaticResource DefaultControlPadding}" />
<Setter Property="hc:MenuAttach.PopupVerticalOffset"
Value="-9" />
<Setter Property="hc:MenuAttach.PopupHorizontalOffset"
Value="10" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="Padding"
Value="2,2,2,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContextMenu">
<Border Effect="{StaticResource EffectShadow2}"
Margin="8"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
MaxHeight="{TemplateBinding MaxHeight}"
BorderThickness="1"
BorderBrush="{DynamicResource BorderBrush}">
<ScrollViewer Theme="{StaticResource ScrollViewerUpDown}"
Margin="{TemplateBinding Padding}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
KeyboardNavigation.TabNavigation="Continue" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type ContextMenu}"
BasedOn="{StaticResource ContextMenuBaseStyle}"
TargetType="ContextMenu" />
<ControlTheme x:Key="ContextMenu.Small"
BasedOn="{StaticResource ContextMenuBaseStyle}"
TargetType="ContextMenu">
<Setter Property="hc:MenuAttach.ItemPadding"
Value="6,0" />
<Setter Property="hc:MenuAttach.PopupVerticalOffset"
Value="-3" />
<Setter Property="hc:MenuAttach.PopupHorizontalOffset"
Value="6" />
</ControlTheme>
</ResourceDictionary>
@@ -0,0 +1,161 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:sys="using:System">
<ControlTheme x:Key="MenuItemBaseStyle"
TargetType="MenuItem">
<Setter Property="Padding"
Value="{Binding Path=(hc:MenuAttach.ItemPadding),RelativeSource={RelativeSource AncestorType=MenuBase}}" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="Margin"
Value="0,0,0,2" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="MinWidth"
Value="240" />
<Setter Property="Template">
<ControlTemplate>
<Border Name="root"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid Margin="{TemplateBinding Padding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="Auto"
SharedSizeGroup="MenuItemIGT" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0"
x:Name="PART_ToggleIconPresenter"
IsVisible="False"
Margin="3"
Width="16"
Height="16" />
<ContentControl Grid.Column="0"
Name="PART_IconPresenter"
Width="16"
Height="16"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Icon}" />
<ContentPresenter Name="PART_HeaderPresenter"
Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Center"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}">
<ContentPresenter.DataTemplates>
<DataTemplate DataType="sys:String">
<AccessText Text="{Binding}" />
</DataTemplate>
</ContentPresenter.DataTemplates>
</ContentPresenter>
<TextBlock x:Name="PART_InputGestureText"
Grid.Column="3"
VerticalAlignment="Center"
Text="{TemplateBinding InputGesture, Converter={StaticResource PlatformKeyGestureConverter}}" />
<Path Name="rightArrow"
Grid.Column="4"
Margin="10,0,0,0"
Width="10"
Height="10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Stretch="Uniform"
Data="{StaticResource RightGeometry}"
Fill="{TemplateBinding Foreground}" />
<Popup Grid.Column="1"
Name="PART_Popup"
IsLightDismissEnabled="False"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"
Placement="RightEdgeAlignedTop">
<Border Background="{DynamicResource RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}">
<ScrollViewer Theme="{StaticResource ScrollViewerUpDown}"
Margin="2,2,2,0">
<ItemsPresenter Name="PART_ItemsPresenter"
Grid.IsSharedSizeScope="True"
ItemsPanel="{TemplateBinding ItemsPanel}" />
</ScrollViewer>
</Border>
</Popup>
</Grid>
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^:selected /template/ Border#root">
<Setter Property="Background"
Value="{DynamicResource SecondaryRegionBrush}" />
</Style>
<Style Selector="^:empty /template/ Path#rightArrow">
<Setter Property="IsVisible"
Value="False" />
</Style>
</ControlTheme>
<ControlTheme x:Key="MenuBaseStyle"
TargetType="Menu">
<Setter Property="hc:MenuAttach.ItemPadding"
Value="{StaticResource DefaultControlPadding}" />
<Setter Property="hc:MenuAttach.PopupVerticalOffset"
Value="-9" />
<Setter Property="hc:MenuAttach.PopupHorizontalOffset"
Value="10" />
<Setter Property="Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="Template">
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
KeyboardNavigation.TabNavigation="Continue" />
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type MenuItem}"
BasedOn="{StaticResource MenuItemBaseStyle}"
TargetType="MenuItem" />
<ControlTheme x:Key="{x:Type Menu}"
BasedOn="{StaticResource MenuBaseStyle}"
TargetType="Menu" />
<ControlTheme x:Key="Menu.Small"
BasedOn="{StaticResource MenuBaseStyle}"
TargetType="Menu">
<Setter Property="hc:MenuAttach.ItemPadding"
Value="6,0" />
<Setter Property="hc:MenuAttach.PopupVerticalOffset"
Value="-3" />
<Setter Property="hc:MenuAttach.PopupHorizontalOffset"
Value="6" />
</ControlTheme>
</ResourceDictionary>
@@ -1,5 +1,7 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:converters="using:Avalonia.Controls.Converters">
<ControlTheme x:Key="ScrollBarBaseRepeatButton"
TargetType="RepeatButton">
<Setter Property="Background"
@@ -184,7 +186,7 @@
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type ScrollViewer}"
<ControlTheme x:Key="ScrollViewerNativeBaseStyle"
TargetType="ScrollViewer">
<Setter Property="Background"
Value="Transparent" />
@@ -229,4 +231,73 @@
Value=".8" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type ScrollViewer}"
BasedOn="{StaticResource ScrollViewerNativeBaseStyle}"
TargetType="ScrollViewer" />
<ControlTheme x:Key="ScrollViewerUpDownRepeatButtonStyle"
BasedOn="{StaticResource RepeatButtonIcon}"
TargetType="RepeatButton">
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Height"
Value="10" />
<Setter Property="Padding"
Value="0,2" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
</ControlTheme>
<ControlTheme x:Key="ScrollViewerUpDown"
TargetType="ScrollViewer">
<Setter Property="VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="Focusable"
Value="False" />
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<RepeatButton Theme="{StaticResource ScrollViewerUpDownRepeatButtonStyle}"
hc:IconElement.Geometry="{StaticResource UpGeometry}"
Command="{Binding LineUp, RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Top">
<RepeatButton.IsVisible>
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}"
ConverterParameter="0">
<Binding Path="VerticalScrollBarVisibility"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Offset.Y"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Extent.Height"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Viewport.Height"
RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</RepeatButton.IsVisible>
</RepeatButton>
<RepeatButton Theme="{StaticResource ScrollViewerUpDownRepeatButtonStyle}"
hc:IconElement.Geometry="{StaticResource DownGeometry}"
Command="{Binding LineDown, RelativeSource={RelativeSource TemplatedParent}}"
DockPanel.Dock="Bottom">
<RepeatButton.IsVisible>
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}"
ConverterParameter="100">
<Binding Path="VerticalScrollBarVisibility"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Offset.Y"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Extent.Height"
RelativeSource="{RelativeSource TemplatedParent}" />
<Binding Path="Viewport.Height"
RelativeSource="{RelativeSource TemplatedParent}" />
</MultiBinding>
</RepeatButton.IsVisible>
</RepeatButton>
<ScrollContentPresenter Name="PART_ContentPresenter"
Margin="{TemplateBinding Padding}" />
</DockPanel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>
@@ -1,7 +1,7 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol">
<ControlTheme x:Key="{x:Type ToolTip}"
<ControlTheme x:Key="ToolTipBaseStyle"
TargetType="ToolTip">
<Setter Property="Padding"
Value="{StaticResource DefaultControlPadding}" />
@@ -34,4 +34,9 @@
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type ToolTip}"
BasedOn="{StaticResource ToolTipBaseStyle}"
TargetType="ToolTip" />
</ResourceDictionary>
@@ -42,6 +42,8 @@
<MergeResourceInclude Source="/Themes/Styles/ButtonSpinner.axaml" />
<MergeResourceInclude Source="/Themes/Styles/NumericUpDown.axaml" />
<MergeResourceInclude Source="/Themes/Styles/ComboBox.axaml" />
<MergeResourceInclude Source="/Themes/Styles/Menu.axaml" />
<MergeResourceInclude Source="/Themes/Styles/ContextMenu.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Styles.Resources>