using MultiTerm.Wpf.ValueConverters; using System.Linq; 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 SingleSelectSubMenu : 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(SingleSelectSubMenu), new PropertyMetadata(String.Empty, OnTitleChanged)); public static readonly DependencyProperty OptionsSourceProperty = DependencyProperty.Register("OptionsSource", typeof(IEnumerable), typeof(SingleSelectSubMenu), new PropertyMetadata(null, OnOptionsSourceChanged)); public static readonly DependencyProperty SelectedMenuItemProperty = DependencyProperty.Register("SelectedMenuItem", typeof(object), typeof(SingleSelectSubMenu), new PropertyMetadata(null, OnSelectedMenuItemPropertyChanged)); #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 SelectedMenuItem. /// [Bindable(true)] public object SelectedMenuItem { get { return GetValue(SelectedMenuItemProperty); } set { SetValue(SelectedMenuItemProperty, value); } } /// /// .NET Property for Title. /// [Bindable(false)] public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } #endregion /// /// SelectedMenuItem Property Changed Handler. /// Updates IsChecked Property of according menu item. /// private static void OnSelectedMenuItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // extract instance and guard null if (d is not SingleSelectSubMenu sssm) { return; } // extract instance of new Value and guard null if (e.NewValue is not MenuItem menuItem) { return; } // get associated menu items (same group) var associatedMenuitems = GetAssociatedMenuItems(sssm); // check menu item with same name in same group foreach (var associatedItem in associatedMenuitems) { if (String.Compare(associatedItem.Header.ToString(), menuItem.Header.ToString(), true) == 0) { associatedItem.IsChecked = true; break; } } } /// /// Title Changed Handler. /// Disables SingleSelectSubMenu and sets header to title. /// private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // extract instance and guard null if (d is not SingleSelectSubMenu sssm) { return; } sssm.Header = sssm.Title; sssm.IsEnabled = false; } /// /// 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 SingleSelectSubMenu sssm) { return; } // extract parent instance of SSSM and guard null if (sssm.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 = true; newMenuItem.StaysOpenOnClick = true; // assign to event handler and register in dictionary newMenuItem.Checked += OnAnyItemChecked; RegisteredSubItemsAndParent.Add(newMenuItem, sssm); // add to parent (which is expected to be a menu item) parent.Items.Add(newMenuItem); } } /// /// Event handler that handles registered menu items being checked. /// /// MenuItem that was checked /// routed event args private static void OnAnyItemChecked(object sender, RoutedEventArgs e) { // extract sender menuItem and guard null if (sender is not MenuItem menuItem) { return; } // get associated menu items var associatedMenuitems = GetAssociatedMenuItems(menuItem); foreach (var associatedItem in associatedMenuitems) { // uncheck items that are not null and not the sender item if (associatedItem != null && associatedItem != menuItem) { associatedItem.IsChecked = false; } } // update SelectedMenuItem for respective parent GetParentSingleSelectSubMenu(menuItem).SelectedMenuItem = menuItem; } #region Helpers private static IEnumerable GetAssociatedMenuItems(SingleSelectSubMenu sssm) { List menuItems = new(); foreach(var item in RegisteredSubItemsAndParent.Where(x => x.Value == sssm)) { menuItems.Add(item.Key); } return menuItems; } private static IEnumerable GetAssociatedMenuItems(MenuItem menuItem) { List menuItems = new(); SingleSelectSubMenu parent = GetParentSingleSelectSubMenu(menuItem); foreach (var item in RegisteredSubItemsAndParent.Where(x => x.Value == parent)) { menuItems.Add(item.Key); } return menuItems; } private static SingleSelectSubMenu GetParentSingleSelectSubMenu(MenuItem menuItem) { return RegisteredSubItemsAndParent[menuItem]; } #endregion }