From 637eb2038643fb18c2575fd1815bacdf3f56f19c Mon Sep 17 00:00:00 2001 From: Jonas Arnold Date: Mon, 15 May 2023 21:36:48 +0200 Subject: [PATCH] manually implemented reading method to get enum value by matching description, removing dependency Humanizer, fixed various warnings --- Common/Helpers/EnumHelpers.cs | 89 +++++++++++++++++-- MultiTerm.Core/ViewModel/TerminalViewModel.cs | 2 +- .../Behaviours/ProgressBarSmoother.cs | 3 +- MultiTerm.Wpf/Behaviours/TabContent.cs | 19 ++-- MultiTerm.Wpf/MultiTerm.Wpf.csproj | 1 - .../EnumDescriptionToMenuItemConverter.cs | 77 +--------------- .../EnumValueToEnumDescriptionConverter.cs | 20 +++-- MultiTerm.Wpf/View/SendReceiveView.xaml | 32 +++---- 8 files changed, 129 insertions(+), 114 deletions(-) diff --git a/Common/Helpers/EnumHelpers.cs b/Common/Helpers/EnumHelpers.cs index 9403b8c..3725bfb 100644 --- a/Common/Helpers/EnumHelpers.cs +++ b/Common/Helpers/EnumHelpers.cs @@ -6,9 +6,11 @@ namespace Common.Helpers; public static class EnumHelpers { /// - /// Parse enum from string to enum type. + /// Parse enum from string to enum enumType. + /// String must be exactly the same as generated by the default Enum.ToString() + /// method and cannot be a searchedDescription of the Enum value. /// - /// type of the enum + /// enumType of the enum /// value to parse to enum /// returns enum object with value public static T ParseEnum(string value) @@ -17,7 +19,24 @@ public static class EnumHelpers } /// - /// Gets the description of an Enum value. + /// Creates an instance of the given if it is an enum type. + /// Throws an exception if it is not an enum type. + /// + /// type of target enum + /// instance of enum tye with default value + public static Enum CreateInstanceOfEnumType(Type enumType) + { + // generate instance of enum using target enumType + if (Activator.CreateInstance(enumType) is not Enum enumObj) + { + throw new Exception($"'{nameof(CreateInstanceOfEnumType)}()' could not instanciate Enum of type {enumType}."); + } + + return enumObj; + } + + /// + /// Gets the searchedDescription of an Enum value. /// If there is no Description set, the Enum Value will be converted to string. /// /// Enum Object to get Description of. @@ -27,7 +46,7 @@ public static class EnumHelpers // guard argument null if (enumObject == null) { throw new ArgumentNullException(nameof(enumObject)); } - // get field info from enum type + // get field info from enum enumType FieldInfo? fieldInfo = enumObject.GetType().GetField(enumObject.ToString()); // return string of enum value if there is no field info if (fieldInfo == null) @@ -35,14 +54,72 @@ public static class EnumHelpers return enumObject.ToString(); } - // get description attribute and return if it is present + // get searchedDescription attribute and return if it is present DescriptionAttribute? descAttrib = (DescriptionAttribute?)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute), true); if (descAttrib != null) { return descAttrib.Description; } - // if no description attribute was found => return string of enum value + // if no searchedDescription attribute was found => return string of enum value return enumObject.ToString(); } + + /// + /// Gets all Description Attributes of a Enum enumType. + /// Descriptions must be unique! + /// + /// An object of the Enum. Will be searched for other Descriptions + /// Key Value Pair of Description and respective Enum Value + public static Dictionary GetAllEnumDescriptions(Enum enumObject) + { + // guard argument null + if (enumObject == null) { throw new ArgumentNullException(nameof(enumObject)); } + + Dictionary descriptionsToEnumValues = new(); + + // get members of enum enumType + var members = enumObject.GetType().GetMembers(); + foreach (var member in members) + { + // get searchedDescription attributes of all members + DescriptionAttribute? descAttrib = (DescriptionAttribute?)member.GetCustomAttribute(typeof(DescriptionAttribute), true); + if (descAttrib != null) + { + // if a searchedDescription exists, add the searchedDescription and the enum value to the dictionary + descriptionsToEnumValues.Add(descAttrib.Description, (Enum)Enum.Parse(enumObject.GetType(), member.Name)); + } + } + + return descriptionsToEnumValues; + } + + /// + /// Searches and returns the Enum Value of which the equals the . + /// Descriptions of enumType of must be uniqe! + /// + /// any instance of the enum enumType + /// description that is compared to the + /// enum object of enumType of or null if no enum with matching description found + public static Enum? GetEnumValueByDescription(Enum enumObj, string searchedDescription) + { + // guard null + if(enumObj == null) { return null; } + + // get all descriptions of the enum + var descriptions = GetAllEnumDescriptions(enumObj); + + // return if equal + foreach (var desc in descriptions) + { + if (desc.Key.Equals(searchedDescription)) + { + return desc.Value; + } + } + + // nothing found + return null; + //throw new Exception($"{nameof(GetEnumValueByDescription)} could not find an enum value in enumType {typeof(enumObj)} that has the Description {searchedDescription}."); + } } diff --git a/MultiTerm.Core/ViewModel/TerminalViewModel.cs b/MultiTerm.Core/ViewModel/TerminalViewModel.cs index 2bf7392..4c5acd6 100644 --- a/MultiTerm.Core/ViewModel/TerminalViewModel.cs +++ b/MultiTerm.Core/ViewModel/TerminalViewModel.cs @@ -174,7 +174,7 @@ public abstract partial class TerminalViewModel : ObservableObject, ITerminalVie { // update internal state this.CommunicationProtocolConnectionState = e; - this.CommunicationProtocolLongIdentifier = this.CommunicationProtocol.LongInstanceIdentifier; + this.CommunicationProtocolLongIdentifier = this.CommunicationProtocol!.LongInstanceIdentifier; // If ViewModel still displays connected after unintentional disconnect => force to disconnected state // allows user to change settings diff --git a/MultiTerm.Wpf/Behaviours/ProgressBarSmoother.cs b/MultiTerm.Wpf/Behaviours/ProgressBarSmoother.cs index 00e8dbb..b5f8d87 100644 --- a/MultiTerm.Wpf/Behaviours/ProgressBarSmoother.cs +++ b/MultiTerm.Wpf/Behaviours/ProgressBarSmoother.cs @@ -6,7 +6,8 @@ using System.Windows; namespace MultiTerm.Wpf.Behaviours; /// -/// From https://stackoverflow.com/questions/14485818/how-to-update-a-progress-bar-so-it-increases-smoothly +/// Smoothens by adding an animation for each change of value. +/// Inspired by https://stackoverflow.com/questions/14485818/how-to-update-a-progress-bar-so-it-increases-smoothly /// public class ProgressBarSmoother { diff --git a/MultiTerm.Wpf/Behaviours/TabContent.cs b/MultiTerm.Wpf/Behaviours/TabContent.cs index c1e57c5..bad42bc 100644 --- a/MultiTerm.Wpf/Behaviours/TabContent.cs +++ b/MultiTerm.Wpf/Behaviours/TabContent.cs @@ -125,8 +125,7 @@ public static class TabContent { if (obj == null) return; - var tabControl = obj as TabControl; - if (tabControl == null) + if (obj is not TabControl tabControl) { throw new InvalidOperationException("Cannot set TabContent.IsCached on object of type " + args.NewValue.GetType().Name + ". Only objects of type TabControl can have TabContent.IsCached property."); @@ -154,9 +153,10 @@ public static class TabContent const string xaml = ""; - var context = new ParserContext(); - - context.XamlTypeMapper = new XamlTypeMapper(new string[0]); + var context = new ParserContext + { + XamlTypeMapper = new XamlTypeMapper(Array.Empty()) + }; context.XamlTypeMapper.AddMappingProcessingInstruction("b", typeof(TabContent).Namespace, typeof(TabContent).Assembly.FullName); context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); @@ -169,9 +169,8 @@ public static class TabContent private static void OnInternalTabControlChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (obj == null) return; - var container = obj as Decorator; - if (container == null) + if (obj is not Decorator container) { var message = "Cannot set TabContent.InternalTabControl on object of type " + obj.GetType().Name + ". Only controls that derive from Decorator, such as Border can have a TabContent.InternalTabControl."; @@ -179,7 +178,7 @@ public static class TabContent } if (args.NewValue == null) return; - if (!(args.NewValue is TabControl)) + if (args.NewValue is not TabControl) { throw new InvalidOperationException("Value of TabContent.InternalTabControl cannot be of type " + args.NewValue.GetType().Name +", it must be of type TabControl"); } @@ -230,7 +229,7 @@ public static class TabContent public class ContentManager { - TabControl _tabControl; + readonly TabControl _tabControl; Decorator _border; public ContentManager(TabControl tabControl, Decorator border) @@ -253,7 +252,7 @@ public static class TabContent _border.Child = GetCurrentContent(); } - private ContentControl GetCurrentContent() + private ContentControl? GetCurrentContent() { var item = _tabControl.SelectedItem; if (item == null) return null; diff --git a/MultiTerm.Wpf/MultiTerm.Wpf.csproj b/MultiTerm.Wpf/MultiTerm.Wpf.csproj index becc936..4d19b19 100644 --- a/MultiTerm.Wpf/MultiTerm.Wpf.csproj +++ b/MultiTerm.Wpf/MultiTerm.Wpf.csproj @@ -15,7 +15,6 @@ - diff --git a/MultiTerm.Wpf/ValueConverters/EnumDescriptionToMenuItemConverter.cs b/MultiTerm.Wpf/ValueConverters/EnumDescriptionToMenuItemConverter.cs index 9bced4b..1b930f6 100644 --- a/MultiTerm.Wpf/ValueConverters/EnumDescriptionToMenuItemConverter.cs +++ b/MultiTerm.Wpf/ValueConverters/EnumDescriptionToMenuItemConverter.cs @@ -1,10 +1,8 @@ using System; -using System.ComponentModel; using System.Globalization; -using System.Reflection; using System.Windows.Data; -using System.Collections.Generic; using System.Windows.Controls; +using Common.Helpers; namespace MultiTerm.Wpf.ValueConverters; @@ -16,65 +14,6 @@ namespace MultiTerm.Wpf.ValueConverters; [ValueConversion(typeof(Enum), typeof(MenuItem))] public class EnumDescriptionToMenuItemConverter : IValueConverter { - /// - /// Gets the description of an Enum value. - /// If there is no Description set, the Enum Value will be converted to string. - /// - /// Enum Object to get Description of. - /// String with Content of DescriptionAttribute of Enum object. - private static string GetEnumDescription(Enum enumObject) - { - // guard argument null - if(enumObject == null) { throw new ArgumentNullException(nameof(enumObject)); } - - // get field info from enum type - FieldInfo? fieldInfo = enumObject.GetType().GetField(enumObject.ToString()); - // return string of enum value if there is no field info - if (fieldInfo == null) - { - return enumObject.ToString(); - } - - // get description attribute and return if it is present - DescriptionAttribute? descAttrib = (DescriptionAttribute?)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute), true); - if (descAttrib != null) - { - return descAttrib.Description; - } - - // if no description attribute was found => return string of enum value - return enumObject.ToString(); - } - - /// - /// Gets all Description Attributes of a Enum type. - /// Descriptions must be unique! - /// - /// An object of the Enum. Will be searched for other Descriptions - /// Key Value Pair of Description and respective Enum Value - private static Dictionary GetAllEnumDescriptions(Enum enumObject) - { - // guard argument null - if (enumObject == null) { throw new ArgumentNullException(nameof(enumObject)); } - - Dictionary descriptionsToEnumValues = new(); - - // get members of enum type - var members = enumObject.GetType().GetMembers(); - foreach (var member in members) - { - // get description attributes of all members - DescriptionAttribute? descAttrib = (DescriptionAttribute?)member.GetCustomAttribute(typeof(DescriptionAttribute), true); - if(descAttrib != null) - { - // if a description exists, add the description and the enum value to the dictionary - descriptionsToEnumValues.Add(descAttrib.Description, (Enum)Enum.Parse(enumObject.GetType(), member.Name)); - } - } - - return descriptionsToEnumValues; - } - /// /// Convert from Data Source to Dependency Object type. /// Here: Enum type to MenuItem. @@ -87,7 +26,7 @@ public class EnumDescriptionToMenuItemConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Enum enumValue = (Enum)value; - string description = GetEnumDescription(enumValue); + string description = EnumHelpers.GetEnumDescription(enumValue); MenuItem newMenuItem = new() { Header = description @@ -106,8 +45,6 @@ public class EnumDescriptionToMenuItemConverter : IValueConverter /// Enum value of the MenuItem that was converted public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - Enum? enumObj; - // guard argument null if (targetType == null) { throw new ArgumentNullException(nameof(targetType)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } @@ -118,15 +55,9 @@ public class EnumDescriptionToMenuItemConverter : IValueConverter throw new Exception($"Cannot convert value that is not of type {nameof(MenuItem)} with {nameof(EnumDescriptionToMenuItemConverter)}"); } - // generate instance of enum using target type - enumObj = Activator.CreateInstance(targetType) as Enum; - if (enumObj == null) - { - throw new Exception($"Could not instanciate Enum of targetType {targetType}."); - } - // get all enum descriptions and iterate - var descriptionToEnumValue = GetAllEnumDescriptions(enumObj); + var enumObj = EnumHelpers.CreateInstanceOfEnumType(targetType); + var descriptionToEnumValue = EnumHelpers.GetAllEnumDescriptions(enumObj); foreach (var kvp in descriptionToEnumValue) { // compare key (enum description) to menu header diff --git a/MultiTerm.Wpf/ValueConverters/EnumValueToEnumDescriptionConverter.cs b/MultiTerm.Wpf/ValueConverters/EnumValueToEnumDescriptionConverter.cs index 74cfb06..5c225ff 100644 --- a/MultiTerm.Wpf/ValueConverters/EnumValueToEnumDescriptionConverter.cs +++ b/MultiTerm.Wpf/ValueConverters/EnumValueToEnumDescriptionConverter.cs @@ -1,9 +1,9 @@ -using Humanizer; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Windows.Data; +using Common.Helpers; namespace MultiTerm.Wpf.ValueConverters; @@ -26,13 +26,13 @@ public class EnumValueToEnumDescriptionConverter : IValueConverter List outputValues = new(); foreach (var item in arrayOfEnumValues) { - outputValues.Add(((Enum)item).Humanize()); + outputValues.Add(EnumHelpers.GetEnumDescription((Enum)item)); } return outputValues; } else if(value is Enum) { - return ((Enum)value).Humanize(); + return EnumHelpers.GetEnumDescription((Enum)value); } else { @@ -43,13 +43,21 @@ public class EnumValueToEnumDescriptionConverter : IValueConverter /// /// Converts single string values to Enum of . /// - /// enum value + /// enum value or empty object if no matching enum value was found /// if value is not a string public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { // guard that it is a string, cannot be a collection if (value is not string stringValue) { throw new ArgumentException("Can only convert string values."); } - return stringValue.DehumanizeTo(targetType); + // find value by description + var objOfEnumType = EnumHelpers.CreateInstanceOfEnumType(targetType); + var enumObjectWithMatchingDescription = EnumHelpers.GetEnumValueByDescription(objOfEnumType, stringValue); + + // return new object if none found + if(enumObjectWithMatchingDescription == null) + { return new object(); } + + return enumObjectWithMatchingDescription; } } diff --git a/MultiTerm.Wpf/View/SendReceiveView.xaml b/MultiTerm.Wpf/View/SendReceiveView.xaml index 599e62b..59e8e37 100644 --- a/MultiTerm.Wpf/View/SendReceiveView.xaml +++ b/MultiTerm.Wpf/View/SendReceiveView.xaml @@ -13,7 +13,7 @@ xmlns:types="clr-namespace:MultiTerm.Core.Types;assembly=MultiTerm.Core" xmlns:settings_view="clr-namespace:MultiTerm.Wpf.View.SettingsView" xmlns:custom_controls="clr-namespace:MultiTerm.Wpf.CustomControl;assembly=MultiTerm.Wpf.CustomControl" - xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors" + xmlns:XamlBehavioursWpf="http://schemas.microsoft.com/xaml/behaviors" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> @@ -80,11 +80,11 @@ - - - - - + + + + + @@ -103,11 +103,11 @@ - - - - - + + + + + @@ -115,11 +115,11 @@ - - - - - + + + + +