using MultiTerm.Wpf.ValueConverters; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Windows; using System.Windows.Controls; using System; namespace MultiTerm.Wpf.Controls; public class CommandableSubMenu : MenuItem { #region Static Properties private static readonly Dictionary RegisteredSubItemsAndParent = new(); #endregion #region Dependency Properties public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(CommandableSubMenu), new PropertyMetadata(String.Empty, OnTitleChanged)); public static readonly DependencyProperty OptionsSourceProperty = DependencyProperty.Register("OptionsSource", typeof(IEnumerable), typeof(CommandableSubMenu), new PropertyMetadata(null, OnOptionsSourceChanged)); public static readonly DependencyProperty CommandParameterTypeProperty = DependencyProperty.Register("CommandParameterType", typeof(Type), typeof(CommandableSubMenu), new PropertyMetadata(null, OnCommandParameterTypePropertyChanged)); #endregion #region Dotnet Properties /// /// .NET Property for OptionsSource. /// [Bindable(true)] public IEnumerable OptionsSource { get { return (IEnumerable)GetValue(OptionsSourceProperty); } set { SetValue(OptionsSourceProperty, value); } } /// /// .NET Property for Title. /// [Bindable(false)] public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } /// /// .NET Property for CommandParameterType. /// [Bindable(false)] public Type CommandParameterType { get { return (Type)GetValue(CommandParameterTypeProperty); } set { SetValue(CommandParameterTypeProperty, value);} } #endregion /// /// Title Changed Handler. /// Disables CommandableSubMenu and sets header to title. /// private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // extract instance and guard null if (d is not CommandableSubMenu csm) { return; } csm.Header = csm.Title; csm.IsEnabled = true; } private static void OnCommandParameterTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // nothing to do } /// /// Options Source Changed Handler. /// Builds list with options and adds them to parent ItemsControl (must be subtype of ItemsControl). /// Registers menu Items in locally stored list. /// Cannot handle changing OptionsSources. Internal list will build up. /// private static void OnOptionsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // extract instance and guard null if (d is not CommandableSubMenu csm) { return; } // extract parent instance of CSM and guard null if (csm.Parent is not ItemsControl parent) { throw new ArgumentException($"Wrong parent type."); } // iterate through new OptionsSource Values and build up list of menuItems foreach (var item in (IEnumerable)e.NewValue) { // create new menu item var converter = new EnumDescriptionToMenuItemConverter(); var newMenuItem = (MenuItem)converter.Convert(item, typeof(MenuItem), new object(), CultureInfo.CurrentCulture); newMenuItem.IsCheckable = false; // assign to event handler and register in dictionary newMenuItem.Click += OnAnyItemClicked; RegisteredSubItemsAndParent.Add(newMenuItem, csm); // add to parent (which is expected to be a menu item) parent.Items.Add(newMenuItem); } } /// /// Event handler that handles registered menu items being clicked. /// /// MenuItem that was clicked /// routed event args private static void OnAnyItemClicked(object sender, RoutedEventArgs e) { // extract sender menuItem and guard null if (sender is not MenuItem menuItem) { return; } // get parent csm var parentCsm = GetParentCommandableSubMenu(menuItem); if (parentCsm.CommandParameterType == null) { throw new Exception($"'OnAnyItemClicked()' Parent CommandableSubMenu has Property {nameof(CommandParameterType)} not set."); } // convert back to enum type var converter = new EnumDescriptionToMenuItemConverter(); var enumValue = (Enum)converter.ConvertBack(menuItem, parentCsm.CommandParameterType, new object(), CultureInfo.CurrentCulture); // run command if not null parentCsm.Command?.Execute(enumValue); } #region Helpers private static CommandableSubMenu GetParentCommandableSubMenu(MenuItem menuItem) { return RegisteredSubItemsAndParent[menuItem]; } #endregion }