implemented CommandableSubMenu to generate list of clickable MenuItems and binding to a command with parameter

master
Jonas Arnold 3 years ago
parent d7e0fcaf21
commit b862b61599
  1. 10
      MultiTerm.Core/ViewModel/ShellViewModel.cs
  2. 148
      MultiTerm.Wpf/Controls/CommandableSubMenu.cs
  3. 22
      MultiTerm.Wpf/View/ShellView.xaml

@ -39,11 +39,17 @@ public partial class ShellViewModel : ObservableObject
{ {
this.sendReceiveViewModelFactory = sendReceiveViewModelFactory; this.sendReceiveViewModelFactory = sendReceiveViewModelFactory;
// create a new terminal // create a new terminal
this.AppendTerminal(this.sendReceiveViewModelFactory.Create()); this.AppendConfiguredTerminal(this.sendReceiveViewModelFactory.Create());
} }
[RelayCommand] [RelayCommand]
private void AppendTerminal(ITerminalViewModel? newTerminal) private void AppendTerminalWithSelectedViewType(ProtocolType protocolType)
{
Console.WriteLine($"Type = {protocolType}");
}
private void AppendConfiguredTerminal(ITerminalViewModel? newTerminal)
{ {
// guard null value // guard null value
if(newTerminal == null) { return; } if(newTerminal == null) { return; }

@ -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
}

@ -31,6 +31,13 @@
<x:Type TypeName="core_common:TerminalViewType" /> <x:Type TypeName="core_common:TerminalViewType" />
</ObjectDataProvider.MethodParameters> </ObjectDataProvider.MethodParameters>
</ObjectDataProvider> </ObjectDataProvider>
<ObjectDataProvider x:Key="ProtocolTypeValues"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="core_common:ProtocolType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources> </UserControl.Resources>
@ -81,17 +88,24 @@
</controls:SingleSelectSubMenu> </controls:SingleSelectSubMenu>
<Separator/> <Separator/>
<MenuItem Header="Protocol" IsEnabled="False"/>
<!-- Protocol types --> <!-- Protocol types -->
<controls:CommandableSubMenu Title="Protocol"
OptionsSource="{Binding Source={StaticResource ProtocolTypeValues}}"
Command="{Binding Data.AppendTerminalWithSelectedViewTypeCommand, Source={StaticResource proxy}}"
CommandParameterType="{x:Type core_common:ProtocolType}">
</controls:CommandableSubMenu>
<!--<MenuItem Header="Protocol" IsEnabled="False"/>
<MenuItem Header="Serial" <MenuItem Header="Serial"
Command="{Binding Data.AppendTerminalCommand, Source={StaticResource proxy}}"> Command="{Binding Data.AppendTerminalCommand, Source={StaticResource proxy}}"
<!--TODO implement parameter CommandParameter="{Binding RelativeSource={RelativeSource Self}}">--> CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
</MenuItem> </MenuItem>
<MenuItem Header="USB HID" /> <MenuItem Header="USB HID" />
<MenuItem Header="TCP" /> <MenuItem Header="TCP" />
<MenuItem Header="UCP" /> <MenuItem Header="UCP" />-->
</ContextMenu> </ContextMenu>
</Button.ContextMenu> </Button.ContextMenu>

Loading…
Cancel
Save