feat: resolve highlight binding issue.

This commit is contained in:
rabbitism
2024-02-13 22:51:38 +08:00
parent a302081ef6
commit 7051521040
5 changed files with 145 additions and 64 deletions

View File

@@ -13,6 +13,7 @@
mc:Ignorable="d">
<ScrollViewer>
<StackPanel HorizontalAlignment="Left">
<!--
<TextBlock Text="{ReflectionBinding #menu2.SelectedItem.Header}"></TextBlock>
<u:NavMenu Name="menu2" Width="300" IsHorizontalCollapsed="{Binding #collapse.IsChecked}">
<u:NavMenuItem Header="Menu 1">
@@ -44,29 +45,32 @@
</u:NavMenuItem>
</u:NavMenu>
<u:Divider Content="Divider" />
-->
<TextBlock Text="{ReflectionBinding #menu.SelectedItem.Header}" />
<ToggleButton Name="collapse">Collapse</ToggleButton>
<u:NavMenu
Name="menu"
Width="300"
HeaderBinding="{Binding Header}"
IconBinding="{Binding}"
IsHorizontalCollapsed="{Binding #collapse.IsChecked, Mode=OneWay}"
ItemsSource="{Binding MenuItems}"
HeaderBinding="{Binding Header}"
SubMenuBinding="{Binding Children}"
IconBinding="{Binding}">
SubMenuBinding="{Binding Children}">
<u:NavMenu.Styles>
<Style Selector="Rectangle">
<Setter Property="Fill" Value="Blue"></Setter>
<Setter Property="Fill" Value="Blue" />
</Style>
<Style Selector="Rectangle.Active">
<Setter Property="Fill" Value="Red"></Setter>
<Setter Property="Fill" Value="Red" />
</Style>
</u:NavMenu.Styles>
<u:NavMenu.IconTemplate>
<DataTemplate>
<Rectangle
Classes.Active="{Binding $parent[u:NavMenuItem].IsHighlighted}"
Width="10" Height="10" >
Width="10"
Height="10"
Classes.Active="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=u:NavMenuItem}, Path=IsHighlighted, Mode=TwoWay}">
</Rectangle>
</DataTemplate>
</u:NavMenu.IconTemplate>

View File

@@ -1,5 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Ursa.Demo.ViewModels;
@@ -12,4 +15,13 @@ public partial class NavMenuDemo : UserControl
InitializeComponent();
this.DataContext = new NavMenuDemoViewModel();
}
private void InputElement_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (sender is Rectangle c)
{
c.ApplyStyling();
var ancestors = c.GetLogicalAncestors();
}
}
}

View File

@@ -26,6 +26,13 @@
Grid.Row="0"
Background="{TemplateBinding u:NavMenuItem.Background}"
CornerRadius="4">
<Border.ContextFlyout>
<Flyout>
<Border>
<StackPanel Name="PART_OverflowPanel"></StackPanel>
</Border>
</Flyout>
</Border.ContextFlyout>
<Grid HorizontalAlignment="Stretch">
<Grid.Margin>
<MultiBinding Converter="{StaticResource NavMarginConverter}">
@@ -66,54 +73,47 @@
</PathIcon>
</Grid>
</Border>
<LayoutTransformControl
Name="PART_Transform"
<ItemsPresenter
Name="PART_ItemsPresenter"
Grid.Row="1"
VerticalAlignment="Top"
RenderTransformOrigin="0.5,0">
<ItemsPresenter Grid.IsSharedSizeScope="True" ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}" />
<LayoutTransformControl.Transitions>
Grid.IsSharedSizeScope="True"
ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}"
RenderTransformOrigin="0.5,0" >
<ItemsPresenter.Transitions>
<Transitions>
<TransformOperationsTransition Property="LayoutTransform" Duration="0.1" />
<DoubleTransition Property="Height" Duration="0.1"></DoubleTransition>
<TransformOperationsTransition Property="RenderTransform" Duration="0.1" />
</Transitions>
</LayoutTransformControl.Transitions>
</LayoutTransformControl>
</ItemsPresenter.Transitions>
</ItemsPresenter>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="TopLevelCollapsedNavMenuItemTemplate" TargetType="u:NavMenuItem">
<Border
Name="PART_Border"
CornerRadius="4"
MinWidth="32"
MinHeight="32"
HorizontalAlignment="Left"
Background="{TemplateBinding u:NavMenuItem.Background}"
HorizontalAlignment="Left">
CornerRadius="4">
<Grid>
<ContentPresenter
Name="PART_IconPresenter"
Content="{Binding Icon, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding IconTemplate, RelativeSource={RelativeSource TemplatedParent}}" />
<Popup
Name="PART_Popup"
IsLightDismissEnabled="True"
Placement="RightEdgeAlignedTop"
PlacementTarget="{Binding #PART_IconPresenter}">
<Border Theme="{DynamicResource CardBorder}" Grid.IsSharedSizeScope="True" Padding="4">
<ItemsPresenter ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
</Popup>
</Grid>
</Border>
</ControlTemplate>
<ControlTemplate x:Key="CollapsedNavMenuItemTemplate" TargetType="u:NavMenuItem">
<Border
Name="PART_Border"
CornerRadius="4"
MinWidth="24"
MinHeight="24"
HorizontalAlignment="Stretch"
Background="{TemplateBinding u:NavMenuItem.Background}"
HorizontalAlignment="Stretch">
<Grid >
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Icon" />
<ColumnDefinition Width="*" />
@@ -137,16 +137,19 @@
Width="8"
Height="8"
Margin="12,0"
RenderTransform="rotate(-90deg)"
Data="{DynamicResource NavigationMenuItemExpandIconGlyph}"
Foreground="{DynamicResource NavigationMenuItemExpandIconForeground}">
</PathIcon>
<Popup Grid.Column="0"
Foreground="{DynamicResource NavigationMenuItemExpandIconForeground}"
RenderTransform="rotate(-90deg)" />
<Popup
Name="PART_Popup"
Grid.Column="0"
IsLightDismissEnabled="True"
Placement="RightEdgeAlignedTop"
PlacementTarget="{Binding #PART_Border}">
<Border Theme="{DynamicResource CardBorder}" Grid.IsSharedSizeScope="True" Padding="4">
<Border
Padding="4"
Grid.IsSharedSizeScope="True"
Theme="{DynamicResource CardBorder}">
<ItemsPresenter ItemsPanel="{Binding ItemsPanel, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
</Popup>
@@ -154,14 +157,20 @@
</Border>
</ControlTemplate>
<ControlTheme x:Key="{x:Type u:NavMenuItem}" TargetType="u:NavMenuItem">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Background" Value="Transparent" />
<Setter Property="ItemsPanel">
<ItemsPanelTemplate>
<u:OverflowStackPanel></u:OverflowStackPanel>
</ItemsPanelTemplate>
</Setter>
<Style Selector="^">
<Setter Property="Template" Value="{StaticResource DefaultNavMenuItemTemplate}" />
<Style Selector="^:selected">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemSelectedBackground}" />
</Style>
<Style Selector="^:vertical-collapsed /template/ LayoutTransformControl#PART_Transform">
<Setter Property="LayoutTransform" Value="scale(1,0)" />
<Style Selector="^:vertical-collapsed /template/ ItemsPresenter#PART_ItemsPresenter">
<Setter Property="Height" Value="0"></Setter>
<Setter Property="RenderTransform" Value="scale(1,0)" />
</Style>
<Style Selector="^:vertical-collapsed /template/ PathIcon#PART_ExpanderIcon">
<Setter Property="RenderTransform" Value="rotate(180deg)" />
@@ -170,18 +179,18 @@
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^ /template/ Border#PART_Border:pointerover">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}"></Setter>
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
</Style>
</Style>
<!--
<Style Selector="^:horizontal-collapsed:first-level">
<Setter Property="Template" Value="{StaticResource TopLevelCollapsedNavMenuItemTemplate}" />
<Style Selector="^ /template/ Border#PART_Border">
<Setter Property="ToolTip.Tip" Value="{Binding Header, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter Property="ToolTip.ShowDelay" Value="0"/>
<Setter Property="ToolTip.Tip" Value="{Binding Header, RelativeSource={RelativeSource TemplatedParent}}" />
<Setter Property="ToolTip.ShowDelay" Value="0" />
</Style>
<Style Selector="^/template/ Border#PART_Border:pointerover">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}"></Setter>
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
</Style>
</Style>
<Style Selector="^:horizontal-collapsed:not(:first-level)">
@@ -193,9 +202,10 @@
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="^/template/ Border#PART_Border:pointerover">
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}"></Setter>
<Setter Property="Background" Value="{DynamicResource NavigationMenuItemPointeroverBackground}" />
</Style>
</Style>
-->
</ControlTheme>
</ResourceDictionary>

View File

@@ -5,7 +5,9 @@ using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using Irihi.Avalonia.Shared.Helpers;
@@ -27,6 +29,8 @@ public class NavMenuItem: HeaderedSelectingItemsControl
private NavMenu? _rootMenu;
private Panel? _popupPanel;
private Popup? _popup;
private Panel? _overflowPanel;
private Border? _border;
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<NavMenuItem, object?>(
nameof(Icon));
@@ -74,8 +78,10 @@ public class NavMenuItem: HeaderedSelectingItemsControl
private bool _isHighlighted;
public static readonly DirectProperty<NavMenuItem, bool> IsHighlightedProperty = AvaloniaProperty.RegisterDirect<NavMenuItem, bool>(
nameof(IsHighlighted), o => o.IsHighlighted, (o, v) => o.IsHighlighted = v);
public static readonly DirectProperty<NavMenuItem, bool> IsHighlightedProperty =
AvaloniaProperty.RegisterDirect<NavMenuItem, bool>(
nameof(IsHighlighted), o => o.IsHighlighted, (o, v) => o.IsHighlighted = v,
defaultBindingMode: BindingMode.TwoWay);
public bool IsHighlighted
{
@@ -129,6 +135,26 @@ public class NavMenuItem: HeaderedSelectingItemsControl
PropertyToPseudoClassMixin.Attach<NavMenuItem>(IsHorizontalCollapsedProperty, PC_HorizontalCollapsed);
PropertyToPseudoClassMixin.Attach<NavMenuItem>(IsVerticalCollapsedProperty, PC_VerticalCollapsed);
PropertyToPseudoClassMixin.Attach<NavMenuItem>(IsSelectedProperty, ":selected", IsSelectedChangedEvent);
IsHorizontalCollapsedProperty.Changed.AddClassHandler<NavMenuItem, bool>((item, args) =>
item.OnIsHorizontalCollapsedChanged(args));
}
private void OnIsHorizontalCollapsedChanged(AvaloniaPropertyChangedEventArgs<bool> args)
{
if (args.NewValue.Value)
{
if (this.ItemsPanelRoot is OverflowStackPanel s)
{
s.MoveChildrenToOverflowPanel();
}
}
else
{
if (this.ItemsPanelRoot is OverflowStackPanel s)
{
s.MoveChildrenToMainPanel();
}
}
}
private void OnLevelChange(AvaloniaPropertyChangedEventArgs<int> args)
@@ -150,6 +176,15 @@ public class NavMenuItem: HeaderedSelectingItemsControl
{
base.OnAttachedToVisualTree(e);
_rootMenu = GetRootMenu();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
SetCurrentValue(LevelProperty,CalculateDistanceFromLogicalParent<NavMenu>(this));
_popup = e.NameScope.Find<Popup>("PART_Popup");
_overflowPanel = e.NameScope.Find<Panel>("PART_OverflowPanel");
_border = e.NameScope.Find<Border>("PART_Border");
if (_rootMenu is not null)
{
if (_rootMenu.IconBinding is not null)
@@ -172,20 +207,17 @@ public class NavMenuItem: HeaderedSelectingItemsControl
this[!HeaderTemplateProperty] = _rootMenu[!NavMenu.HeaderTemplateProperty];
this[!SubMenuIndentProperty] = _rootMenu[!NavMenu.SubMenuIndentProperty];
this[!IsHorizontalCollapsedProperty] = _rootMenu[!NavMenu.IsHorizontalCollapsedProperty];
if (this == _rootMenu.SelectedItem || this.DataContext == _rootMenu.SelectedItem)
{
SelectItem(this);
}
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
protected override void OnLoaded(RoutedEventArgs e)
{
var children = this.ItemsPanelRoot?.Children.ToList();
base.OnApplyTemplate(e);
SetCurrentValue(LevelProperty,CalculateDistanceFromLogicalParent<NavMenu>(this));
_popup = e.NameScope.Find<Popup>("PART_Popup");
base.OnLoaded(e);
var root = this.ItemsPanelRoot;
if (root is OverflowStackPanel stack)
{
stack.OverflowPanel = _overflowPanel;
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
@@ -203,16 +235,9 @@ public class NavMenuItem: HeaderedSelectingItemsControl
}
else
{
if (_popup is not null)
if (_border?.ContextFlyout is not null)
{
if (_popup.IsOpen)
{
_popup.Close();
}
else
{
_popup.Open();
}
_border.ContextFlyout.ShowAt(this);
}
}
}

View File

@@ -0,0 +1,30 @@
using Avalonia.Controls;
namespace Ursa.Controls;
public class OverflowStackPanel: StackPanel
{
public Panel? OverflowPanel { get; set; }
public void MoveChildrenToOverflowPanel()
{
var children = this.Children.ToList();
foreach (var child in children)
{
this.Children.Remove(child);
this.OverflowPanel?.Children.Add(child);
}
}
public void MoveChildrenToMainPanel()
{
var children = this.OverflowPanel?.Children.ToList();
if (children != null && children.Count > 0)
{
foreach (var child in children)
{
this.OverflowPanel?.Children.Remove(child);
this.Children.Add(child);
}
}
}
}