|
|
|
|
@ -0,0 +1,148 @@ |
|
|
|
|
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<MenuItem, CommandableSubMenu> 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 |
|
|
|
|
/// <summary> |
|
|
|
|
/// .NET Property for OptionsSource. |
|
|
|
|
/// </summary> |
|
|
|
|
[Bindable(true)] |
|
|
|
|
public IEnumerable OptionsSource |
|
|
|
|
{ |
|
|
|
|
get { return (IEnumerable)GetValue(OptionsSourceProperty); } |
|
|
|
|
set { SetValue(OptionsSourceProperty, value); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// .NET Property for Title. |
|
|
|
|
/// </summary> |
|
|
|
|
[Bindable(false)] |
|
|
|
|
public string Title |
|
|
|
|
{ |
|
|
|
|
get { return (string)GetValue(TitleProperty); } |
|
|
|
|
set { SetValue(TitleProperty, value); } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// .NET Property for CommandParameterType. |
|
|
|
|
/// </summary> |
|
|
|
|
[Bindable(false)] |
|
|
|
|
public Type CommandParameterType |
|
|
|
|
{ |
|
|
|
|
get { return (Type)GetValue(CommandParameterTypeProperty); } |
|
|
|
|
set { SetValue(CommandParameterTypeProperty, value);} |
|
|
|
|
} |
|
|
|
|
#endregion |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Title Changed Handler. |
|
|
|
|
/// Disables CommandableSubMenu and sets header to title. |
|
|
|
|
/// </summary> |
|
|
|
|
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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 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. |
|
|
|
|
/// </summary> |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Event handler that handles registered menu items being clicked. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="sender">MenuItem that was clicked</param> |
|
|
|
|
/// <param name="e">routed event args</param> |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
} |