WIP: inherit binding from root.
This commit is contained in:
@@ -1,27 +1,54 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
x:Class="Ursa.Demo.Pages.NavMenuDemo"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:u="https://irihi.tech/ursa"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
x:Class="Ursa.Demo.Pages.NavMenuDemo">
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
<u:NavMenu Name="menu">
|
xmlns:vm="using:Ursa.Demo.ViewModels"
|
||||||
<u:NavMenuItem Header="Menu 1">
|
d:DesignHeight="450"
|
||||||
<u:NavMenuItem.Icon>
|
d:DesignWidth="800"
|
||||||
<Rectangle Width="10" Height="10" Fill="Red"></Rectangle>
|
x:CompileBindings="True"
|
||||||
</u:NavMenuItem.Icon>
|
x:DataType="vm:NavMenuDemoViewModel"
|
||||||
</u:NavMenuItem>
|
mc:Ignorable="d">
|
||||||
<u:NavMenuItem Header="Menu 2">
|
<StackPanel>
|
||||||
<u:NavMenuItem.Icon>
|
<u:NavMenu>
|
||||||
<Rectangle Width="20" Height="10" Fill="Red"></Rectangle>
|
<u:NavMenuItem Header="Menu 1">
|
||||||
</u:NavMenuItem.Icon>
|
<u:NavMenuItem.Icon>
|
||||||
</u:NavMenuItem>
|
<Rectangle
|
||||||
<u:NavMenuItem Header="Menu 3">
|
Width="10"
|
||||||
<u:NavMenuItem.Icon>
|
Height="10"
|
||||||
<Rectangle Width="30" Height="10" Fill="Red"></Rectangle>
|
Fill="Red" />
|
||||||
</u:NavMenuItem.Icon>
|
</u:NavMenuItem.Icon>
|
||||||
</u:NavMenuItem>
|
<u:NavMenuItem Header="Sub Menu 1"></u:NavMenuItem>
|
||||||
<u:Divider Content="Divider"></u:Divider>
|
<u:NavMenuItem Header="Sub Menu 2"></u:NavMenuItem>
|
||||||
<TextBlock Text="{Binding #menu.SelectedItem}"></TextBlock>
|
<u:NavMenuItem Header="Sub Menu 3"></u:NavMenuItem>
|
||||||
</u:NavMenu>
|
</u:NavMenuItem>
|
||||||
|
<u:NavMenuItem Header="Menu 2">
|
||||||
|
<u:NavMenuItem.Icon>
|
||||||
|
<Rectangle
|
||||||
|
Width="20"
|
||||||
|
Height="10"
|
||||||
|
Fill="Red" />
|
||||||
|
</u:NavMenuItem.Icon>
|
||||||
|
</u:NavMenuItem>
|
||||||
|
<u:NavMenuItem Header="Menu 3">
|
||||||
|
<u:NavMenuItem.Icon>
|
||||||
|
<Rectangle
|
||||||
|
Width="30"
|
||||||
|
Height="10"
|
||||||
|
Fill="Red" />
|
||||||
|
</u:NavMenuItem.Icon>
|
||||||
|
</u:NavMenuItem>
|
||||||
|
</u:NavMenu>
|
||||||
|
<u:Divider Content="Divider" />
|
||||||
|
<TextBlock Text="{ReflectionBinding #menu.SelectedItem.Header}" />
|
||||||
|
<u:NavMenu
|
||||||
|
Name="menu"
|
||||||
|
ItemsSource="{Binding MenuItems}"
|
||||||
|
HeaderBinding="{Binding Header}"
|
||||||
|
SubMenuBinding="{Binding Children}"
|
||||||
|
IconBinding="{Binding Header}"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
namespace Ursa.Demo.Pages;
|
namespace Ursa.Demo.Pages;
|
||||||
|
|
||||||
@@ -9,5 +10,6 @@ public partial class NavMenuDemo : UserControl
|
|||||||
public NavMenuDemo()
|
public NavMenuDemo()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
this.DataContext = new NavMenuDemoViewModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,49 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace Ursa.Demo.ViewModels;
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
public class NavMenuDemoViewModel: ObservableObject
|
public class NavMenuDemoViewModel: ObservableObject
|
||||||
{
|
{
|
||||||
|
public ObservableCollection<MenuItem> MenuItems { get; set; } = new ObservableCollection<MenuItem>
|
||||||
|
{
|
||||||
|
new MenuItem { Header = "Introduction" , Children =
|
||||||
|
{
|
||||||
|
new MenuItem() { Header = "Getting Started" },
|
||||||
|
new MenuItem() { Header = "Design Principles" },
|
||||||
|
new MenuItem() { Header = "Contributing" },
|
||||||
|
}},
|
||||||
|
new MenuItem { Header = "Badge" },
|
||||||
|
new MenuItem { Header = "Banner" },
|
||||||
|
new MenuItem { Header = "ButtonGroup" },
|
||||||
|
new MenuItem { Header = "Class Input" },
|
||||||
|
new MenuItem { Header = "Dialog" },
|
||||||
|
new MenuItem { Header = "Divider" },
|
||||||
|
new MenuItem { Header = "Drawer" },
|
||||||
|
new MenuItem { Header = "DualBadge" },
|
||||||
|
new MenuItem { Header = "EnumSelector" },
|
||||||
|
new MenuItem { Header = "ImageViewer" },
|
||||||
|
new MenuItem { Header = "IPv4Box" },
|
||||||
|
new MenuItem { Header = "IconButton" },
|
||||||
|
new MenuItem { Header = "KeyGestureInput" },
|
||||||
|
new MenuItem { Header = "Loading" },
|
||||||
|
new MenuItem { Header = "MessageBox" },
|
||||||
|
new MenuItem { Header = "Navigation" },
|
||||||
|
new MenuItem { Header = "NavMenu" },
|
||||||
|
new MenuItem { Header = "NumericUpDown" },
|
||||||
|
new MenuItem { Header = "Pagination" },
|
||||||
|
new MenuItem { Header = "RangeSlider" },
|
||||||
|
new MenuItem { Header = "SelectionList" },
|
||||||
|
new MenuItem { Header = "TagInput" },
|
||||||
|
new MenuItem { Header = "Timeline" },
|
||||||
|
new MenuItem { Header = "TwoTonePathIcon" },
|
||||||
|
new MenuItem { Header = "ThemeToggler" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuItem
|
||||||
|
{
|
||||||
|
public string? Header { get; set; }
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
public ObservableCollection<MenuItem> Children { get; set; } = new ObservableCollection<MenuItem>();
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Data;
|
using Avalonia.Data;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.Metadata;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
@@ -18,6 +19,39 @@ public class NavMenu: ItemsControl
|
|||||||
set => SetValue(SelectedItemProperty, value);
|
set => SetValue(SelectedItemProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBinding?> IconBindingProperty = AvaloniaProperty.Register<NavMenu, IBinding?>(
|
||||||
|
nameof(IconBinding));
|
||||||
|
|
||||||
|
[AssignBinding]
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? IconBinding
|
||||||
|
{
|
||||||
|
get => GetValue(IconBindingProperty);
|
||||||
|
set => SetValue(IconBindingProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBinding?> HeaderBindingProperty = AvaloniaProperty.Register<NavMenu, IBinding?>(
|
||||||
|
nameof(HeaderBinding));
|
||||||
|
|
||||||
|
[AssignBinding]
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? HeaderBinding
|
||||||
|
{
|
||||||
|
get => GetValue(HeaderBindingProperty);
|
||||||
|
set => SetValue(HeaderBindingProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBinding?> SubMenuBindingProperty = AvaloniaProperty.Register<NavMenu, IBinding?>(
|
||||||
|
nameof(SubMenuBinding));
|
||||||
|
|
||||||
|
[AssignBinding]
|
||||||
|
[InheritDataTypeFromItems(nameof(ItemsSource))]
|
||||||
|
public IBinding? SubMenuBinding
|
||||||
|
{
|
||||||
|
get => GetValue(SubMenuBindingProperty);
|
||||||
|
set => SetValue(SubMenuBindingProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
static NavMenu()
|
static NavMenu()
|
||||||
{
|
{
|
||||||
SelectedItemProperty.Changed.AddClassHandler<NavMenu, object?>((o, e) => o.OnSelectedItemChange(e));
|
SelectedItemProperty.Changed.AddClassHandler<NavMenu, object?>((o, e) => o.OnSelectedItemChange(e));
|
||||||
@@ -37,7 +71,27 @@ public class NavMenu: ItemsControl
|
|||||||
{
|
{
|
||||||
return new NavMenuItem();
|
return new NavMenuItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.PrepareContainerForItemOverride(container, item, index);
|
||||||
|
if (container is NavMenuItem navMenuItem)
|
||||||
|
{
|
||||||
|
if (IconBinding is not null)
|
||||||
|
{
|
||||||
|
navMenuItem[!NavMenuItem.IconProperty] = IconBinding;
|
||||||
|
}
|
||||||
|
if (HeaderBinding is not null)
|
||||||
|
{
|
||||||
|
navMenuItem[!HeaderedItemsControl.HeaderProperty] = HeaderBinding;
|
||||||
|
}
|
||||||
|
if (SubMenuBinding is not null)
|
||||||
|
{
|
||||||
|
navMenuItem[!ItemsSourceProperty] = SubMenuBinding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void SelectItem(NavMenuItem item)
|
internal void SelectItem(NavMenuItem item)
|
||||||
{
|
{
|
||||||
if (item.IsSelected) return;
|
if (item.IsSelected) return;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Avalonia;
|
using System.Windows.Input;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
using Avalonia.Controls.Mixins;
|
using Avalonia.Controls.Mixins;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
@@ -9,8 +11,11 @@ using Avalonia.VisualTree;
|
|||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
[PseudoClasses(PC_Highlighted)]
|
||||||
public class NavMenuItem: HeaderedSelectingItemsControl
|
public class NavMenuItem: HeaderedSelectingItemsControl
|
||||||
{
|
{
|
||||||
|
public const string PC_Highlighted = "highlighted";
|
||||||
|
|
||||||
private NavMenu? _rootMenu;
|
private NavMenu? _rootMenu;
|
||||||
|
|
||||||
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<NavMenuItem, object?>(
|
public static readonly StyledProperty<object?> IconProperty = AvaloniaProperty.Register<NavMenuItem, object?>(
|
||||||
@@ -31,6 +36,15 @@ public class NavMenuItem: HeaderedSelectingItemsControl
|
|||||||
set => SetValue(IconTemplateProperty, value);
|
set => SetValue(IconTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<ICommand?> CommandProperty = AvaloniaProperty.Register<NavMenuItem, ICommand?>(
|
||||||
|
nameof(Command));
|
||||||
|
|
||||||
|
public ICommand? Command
|
||||||
|
{
|
||||||
|
get => GetValue(CommandProperty);
|
||||||
|
set => SetValue(CommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public new static readonly StyledProperty<bool> IsSelectedProperty =
|
public new static readonly StyledProperty<bool> IsSelectedProperty =
|
||||||
SelectingItemsControl.IsSelectedProperty.AddOwner<NavMenuItem>();
|
SelectingItemsControl.IsSelectedProperty.AddOwner<NavMenuItem>();
|
||||||
|
|
||||||
@@ -39,6 +53,8 @@ public class NavMenuItem: HeaderedSelectingItemsControl
|
|||||||
get => GetValue(IsSelectedProperty);
|
get => GetValue(IsSelectedProperty);
|
||||||
set => SetValue(IsSelectedProperty, value);
|
set => SetValue(IsSelectedProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static NavMenuItem()
|
static NavMenuItem()
|
||||||
{
|
{
|
||||||
@@ -56,26 +72,34 @@ public class NavMenuItem: HeaderedSelectingItemsControl
|
|||||||
return new NavMenuItem();
|
return new NavMenuItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
|
||||||
|
{
|
||||||
|
base.PrepareContainerForItemOverride(container, item, index);
|
||||||
|
if (container is NavMenuItem navMenuItem)
|
||||||
|
{
|
||||||
|
if (_rootMenu?.HeaderBinding is not null)
|
||||||
|
{
|
||||||
|
container[!HeaderProperty] = _rootMenu.HeaderBinding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_rootMenu = GetRootMenu();
|
_rootMenu = GetRootMenu();
|
||||||
UpdateSelection(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnPointerPressed(e);
|
base.OnPointerPressed(e);
|
||||||
_rootMenu?.SelectItem(this);
|
_rootMenu?.SelectItem(this);
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NavMenu? GetRootMenu()
|
private NavMenu? GetRootMenu()
|
||||||
{
|
{
|
||||||
var root = this.FindAncestorOfType<NavMenu>();
|
var root = this.FindAncestorOfType<NavMenu>() ?? this.FindLogicalAncestorOfType<NavMenu>();
|
||||||
if (root is null)
|
|
||||||
{
|
|
||||||
root = this.FindLogicalAncestorOfType<NavMenu>();
|
|
||||||
}
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user