| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Avalonia.Controls.Generators;
- using Avalonia.Controls.Platform;
- using Avalonia.Controls.Primitives;
- using Avalonia.Input;
- using Avalonia.Interactivity;
- using Avalonia.LogicalTree;
- #nullable enable
- namespace Avalonia.Controls
- {
- /// <summary>
- /// Base class for menu controls.
- /// </summary>
- public abstract class MenuBase : SelectingItemsControl, IFocusScope, IMenu
- {
- /// <summary>
- /// Defines the <see cref="IsOpen"/> property.
- /// </summary>
- public static readonly DirectProperty<Menu, bool> IsOpenProperty =
- AvaloniaProperty.RegisterDirect<Menu, bool>(
- nameof(IsOpen),
- o => o.IsOpen);
- /// <summary>
- /// Defines the <see cref="MenuOpened"/> event.
- /// </summary>
- public static readonly RoutedEvent<RoutedEventArgs> MenuOpenedEvent =
- RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble);
- /// <summary>
- /// Defines the <see cref="MenuClosed"/> event.
- /// </summary>
- public static readonly RoutedEvent<RoutedEventArgs> MenuClosedEvent =
- RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble);
- private bool _isOpen;
- /// <summary>
- /// Initializes a new instance of the <see cref="MenuBase"/> class.
- /// </summary>
- public MenuBase()
- {
- InteractionHandler = new DefaultMenuInteractionHandler(false);
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="MenuBase"/> class.
- /// </summary>
- /// <param name="interactionHandler">The menu interaction handler.</param>
- public MenuBase(IMenuInteractionHandler interactionHandler)
- {
- InteractionHandler = interactionHandler ?? throw new ArgumentNullException(nameof(interactionHandler));
- }
- /// <summary>
- /// Initializes static members of the <see cref="MenuBase"/> class.
- /// </summary>
- static MenuBase()
- {
- MenuItem.SubmenuOpenedEvent.AddClassHandler<MenuBase>((x, e) => x.OnSubmenuOpened(e));
- }
- /// <summary>
- /// Gets a value indicating whether the menu is open.
- /// </summary>
- public bool IsOpen
- {
- get { return _isOpen; }
- protected set { SetAndRaise(IsOpenProperty, ref _isOpen, value); }
- }
- /// <inheritdoc/>
- IMenuInteractionHandler IMenu.InteractionHandler => InteractionHandler;
- /// <inheritdoc/>
- IMenuItem? IMenuElement.SelectedItem
- {
- get
- {
- var index = SelectedIndex;
- return (index != -1) ?
- (IMenuItem)ItemContainerGenerator.ContainerFromIndex(index) :
- null;
- }
- set
- {
- SelectedIndex = ItemContainerGenerator.IndexFromContainer(value);
- }
- }
- /// <inheritdoc/>
- IEnumerable<IMenuItem> IMenuElement.SubItems
- {
- get
- {
- return ItemContainerGenerator.Containers
- .Select(x => x.ContainerControl)
- .OfType<IMenuItem>();
- }
- }
- /// <summary>
- /// Gets the interaction handler for the menu.
- /// </summary>
- protected IMenuInteractionHandler InteractionHandler { get; }
- /// <summary>
- /// Occurs when a <see cref="Menu"/> is opened.
- /// </summary>
- public event EventHandler<RoutedEventArgs> MenuOpened
- {
- add { AddHandler(MenuOpenedEvent, value); }
- remove { RemoveHandler(MenuOpenedEvent, value); }
- }
- /// <summary>
- /// Occurs when a <see cref="Menu"/> is closed.
- /// </summary>
- public event EventHandler<RoutedEventArgs> MenuClosed
- {
- add { AddHandler(MenuClosedEvent, value); }
- remove { RemoveHandler(MenuClosedEvent, value); }
- }
- /// <summary>
- /// Closes the menu.
- /// </summary>
- public abstract void Close();
- /// <summary>
- /// Opens the menu.
- /// </summary>
- public abstract void Open();
- /// <inheritdoc/>
- bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap);
- /// <inheritdoc/>
- protected override IItemContainerGenerator CreateItemContainerGenerator()
- {
- return new ItemContainerGenerator<MenuItem>(this, MenuItem.HeaderProperty, null);
- }
- /// <inheritdoc/>
- protected override void OnKeyDown(KeyEventArgs e)
- {
- // Don't handle here: let the interaction handler handle it.
- }
- /// <inheritdoc/>
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnAttachedToVisualTree(e);
- InteractionHandler.Attach(this);
- }
- /// <inheritdoc/>
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnDetachedFromVisualTree(e);
- InteractionHandler.Detach(this);
- }
- /// <summary>
- /// Called when a submenu opens somewhere in the menu.
- /// </summary>
- /// <param name="e">The event args.</param>
- protected virtual void OnSubmenuOpened(RoutedEventArgs e)
- {
- if (e.Source is MenuItem menuItem && menuItem.Parent == this)
- {
- foreach (var child in this.GetLogicalChildren().OfType<MenuItem>())
- {
- if (child != menuItem && child.IsSubMenuOpen)
- {
- child.IsSubMenuOpen = false;
- }
- }
- }
- IsOpen = true;
- }
- }
- }
|