File: Menu\MenuItem.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.Collections.Generic;
using System.Windows.Input;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.StyleSheets;
using Microsoft.Maui.Graphics;
 
namespace Microsoft.Maui.Controls
{
	/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="Type[@FullName='Microsoft.Maui.Controls.MenuItem']/Docs/*" />
	public partial class MenuItem : BaseMenuItem, IMenuItemController, ICommandElement, IMenuElement, IPropertyPropagationController
	{
		/// <summary>Bindable property for <see cref="Accelerator"/>.</summary>
		[Obsolete("Use MenuFlyoutItem.KeyboardAcceleratorsProperty instead.")]
		public static readonly BindableProperty AcceleratorProperty = BindableProperty.CreateAttached(nameof(Accelerator), typeof(Accelerator), typeof(MenuItem), null);
 
		/// <summary>Bindable property for <see cref="Command"/>.</summary>
		public static readonly BindableProperty CommandProperty = BindableProperty.Create(
			nameof(Command), typeof(ICommand), typeof(MenuItem), null,
			propertyChanging: CommandElement.OnCommandChanging,
			propertyChanged: CommandElement.OnCommandChanged);
 
		/// <summary>Bindable property for <see cref="CommandParameter"/>.</summary>
		public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
			nameof(CommandParameter), typeof(object), typeof(MenuItem), null,
			propertyChanged: CommandElement.OnCommandParameterChanged);
 
		/// <summary>Bindable property for <see cref="IsDestructive"/>.</summary>
		public static readonly BindableProperty IsDestructiveProperty = BindableProperty.Create(nameof(IsDestructive), typeof(bool), typeof(MenuItem), false);
 
		/// <summary>Bindable property for <see cref="IconImageSource"/>.</summary>
		public static readonly BindableProperty IconImageSourceProperty = BindableProperty.Create(nameof(IconImageSource), typeof(ImageSource), typeof(MenuItem), default(ImageSource),
			propertyChanged: (bindable, oldValue, newValue) =>
			{
				((MenuItem)bindable).AddRemoveLogicalChildren(oldValue, newValue);
			}
		);
 
		/// <summary>Bindable property for <see cref="IsEnabled"/>.</summary>
		public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create(
			nameof(IsEnabled), typeof(bool), typeof(MenuItem), true,
			propertyChanged: OnIsEnabledPropertyChanged, coerceValue: CoerceIsEnabledProperty);
 
		/// <summary>Bindable property for <see cref="Text"/>.</summary>
		public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(MenuItem), null);
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='GetAccelerator']/Docs/*" />
		[Obsolete("Use MenuFlyoutItem.KeyboardAcceleratorsProperty instead.")]
		public static Accelerator GetAccelerator(BindableObject bindable) => (Accelerator)bindable.GetValue(AcceleratorProperty);
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='SetAccelerator']/Docs/*" />
		[Obsolete("Use MenuFlyoutItem.KeyboardAcceleratorsProperty instead.")]
		public static void SetAccelerator(BindableObject bindable, Accelerator value) => bindable.SetValue(AcceleratorProperty, value);
		bool _isEnabledExplicit = (bool)IsEnabledProperty.DefaultValue;
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='.ctor']/Docs/*" />
		public MenuItem()
		{
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='Command']/Docs/*" />
		public ICommand Command
		{
			get => (ICommand)GetValue(CommandProperty);
			set => SetValue(CommandProperty, value);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='CommandParameter']/Docs/*" />
		public object CommandParameter
		{
			get => GetValue(CommandParameterProperty);
			set => SetValue(CommandParameterProperty, value);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='IconImageSource']/Docs/*" />
		public ImageSource IconImageSource
		{
			get => (ImageSource)GetValue(IconImageSourceProperty);
			set => SetValue(IconImageSourceProperty, value);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='IsDestructive']/Docs/*" />
		public bool IsDestructive
		{
			get => (bool)GetValue(IsDestructiveProperty);
			set => SetValue(IsDestructiveProperty, value);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='Text']/Docs/*" />
		public string Text
		{
			get => (string)GetValue(TextProperty);
			set => SetValue(TextProperty, value);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/MenuItem.xml" path="//Member[@MemberName='IsEnabled']/Docs/*" />
		public bool IsEnabled
		{
			get => (bool)GetValue(IsEnabledProperty);
			set => SetValue(IsEnabledProperty, value);
		}
 
		public event EventHandler Clicked;
 
		protected virtual void OnClicked() => Clicked?.Invoke(this, EventArgs.Empty);
 
		void IMenuItemController.Activate()
		{
			if (IsEnabled)
				Command?.Execute(CommandParameter);
 
			OnClicked();
		}
 
		void ICommandElement.CanExecuteChanged(object sender, EventArgs e) =>
			this.RefreshPropertyValue(IsEnabledProperty, _isEnabledExplicit);
 
		static object CoerceIsEnabledProperty(BindableObject bindable, object value)
		{
			if (bindable is not MenuItem menuItem)
			{
				return false;
			}
 
			menuItem._isEnabledExplicit = (bool)value;
 
			if (!menuItem._isEnabledExplicit)
			{
				// No need to check GetCanExecute or the Parent's state
				return false;
			}
 
			var canExecute = CommandElement.GetCanExecute(menuItem);
			if (!canExecute)
			{
				return false;
			}
 
			// IsEnabled is not explicitly set to false, and the command can be
			// executed. The only thing left to verify is Parent.IsEnabled
			if (menuItem.Parent is MenuItem parentMenuItem && !parentMenuItem.IsEnabled)
			{
				return false;
			}
 
			return true;
		}
 
		IImageSource IImageSourcePart.Source => this.IconImageSource;
 
		bool IImageSourcePart.IsAnimationPlaying => false;
 
		Color ITextStyle.TextColor => null;
 
		Font ITextStyle.Font => Font.Default;
 
		double ITextStyle.CharacterSpacing => 0;
 
		void IMenuElement.Clicked()
		{
			((IMenuItemController)this).Activate();
		}
 
		void IImageSourcePart.UpdateIsLoading(bool isLoading)
		{
		}
 
		void IPropertyPropagationController.PropagatePropertyChanged(string propertyName)
		{
			if (propertyName == null || propertyName == IsEnabledProperty.PropertyName)
				this.RefreshPropertyValue(IsEnabledProperty, _isEnabledExplicit);
 
			PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, ((IVisualTreeElement)this).GetVisualChildren());
		}
 
		static void OnIsEnabledPropertyChanged(BindableObject bindable, object oldValue, object newValue)
		{
			if (bindable is not MenuItem || bindable is not IPropertyPropagationController ppc)
			{
				return;
			}
 
			ppc.PropagatePropertyChanged(IsEnabledProperty.PropertyName);
		}
	}
}