[WIP] Refactor converters and dependant components.

pull/2/head
Matija Koželj 2 years ago
parent 6f91dacb0c
commit be914e4b2e

@ -0,0 +1,226 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 3
indent_style = tab
tab_width = 3
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Expression-level preferences
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = false
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Field preferences
dotnet_style_readonly_field = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = 0
# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = false:suggestion
dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = true:warning
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
# Expression-bodied members
csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion
csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
csharp_style_expression_bodied_properties = when_on_single_line:suggestion
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true
# Null-checking preferences
csharp_style_conditional_delegate_call = true
# Modifier preferences
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_style_prefer_readonly_struct = true
# Code-block preferences
csharp_prefer_braces = when_multiline:suggestion
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_prefer_method_group_conversion = true:suggestion
csharp_style_prefer_top_level_statements = false:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true
csharp_style_inlined_variable_declaration = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:suggestion
# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:suggestion
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = false:suggestion
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = no_change
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

@ -83,7 +83,7 @@ public partial class Autocomplete<T> : InputBase<T>, IDisposable
_toStringFunc = value;
Converter = new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null);
SetConverter(new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null));
}
}
/// <summary>

@ -1,4 +1,5 @@
using Connected.Extensions;
using Connected.Utilities.BindingConverters;
namespace Connected.Components;

@ -68,12 +68,7 @@ public abstract class FormComponent<T, U> : UIComponent, IFormComponent, IDispos
/// <summary>
/// The generic converter of the component.
/// </summary>
[Parameter]
public Converter<T, U> Converter
{
get => _converter;
set => SetConverter(value);
}
public Converter<T, U> Converter => _converter;
/// <summary>
/// The culture of the component. Also sets the culture of the <see cref="Converter"/> .

@ -1,5 +1,6 @@
using System.Timers;
using Connected.Annotations;
using Connected.Utilities.BindingConverters;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;

@ -1,4 +1,5 @@
using Connected.Extensions;
using Connected.Utilities.BindingConverters;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;
@ -10,7 +11,7 @@ public partial class RangeInput<T> : InputBase<Range<T>>
public RangeInput()
{
Value = new Range<T>();
Converter = new RangeConverter<T>();
SetConverter(new RangeConverter<T>());
}
protected string Classname => InputCssHelper.GetClassname(this,

@ -8,6 +8,7 @@ using System.Globalization;
using Connected.Annotations;
using Connected.Services;
using Connected.Utilities;
using Connected.Utilities.BindingConverters;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
@ -208,7 +209,7 @@ public partial class NumericField<T> : DebouncedInput<T>
return (T)(object)Convert.ToInt64(FromInt64(Value) + FromInt64(Step) * factor);
if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
return (T)(object)Convert.ToUInt64(FromUInt64(Value) + FromUInt64(Step) * factor);
return Num.To<T>(Num.From(Value) + Num.From(Step) * factor);
return Number.To<T>(Number.From(Value) + Number.From(Step) * factor);
}
/// <summary>

@ -2,6 +2,7 @@
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Connected.Utilities.BindingConverters;
using Microsoft.AspNetCore.Components;
namespace Connected.Components;

@ -293,7 +293,7 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
if (_toStringFunc == value)
return;
_toStringFunc = value;
Converter = new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null);
SetConverter(new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null));
}
}

@ -1,5 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Globalization;
using System.Text.RegularExpressions;
using Connected.Annotations;
@ -20,7 +19,7 @@ public partial class TimePicker : Picker<TimeSpan?>
{
_timeFormat = format24Hours;
Converter = new LambdaConverter<TimeSpan?, string>((e) => OnSet(e), (e) => OnGet(e));
SetConverter(new LambdaConverter<TimeSpan?, string>((e) => OnSet(e), (e) => OnGet(e)));
AdornmentIcon = Icons.Material.Filled.AccessTime;
AdornmentAriaLabel = "Open Time Picker";

@ -1,38 +1,48 @@
using System.ComponentModel;
namespace Connected
{
public enum InputType
{
[Description("text")]
Text,
[Description("password")]
Password,
[Description("email")]
Email,
[Description("hidden")]
Hidden,
[Description("number")]
Number,
[Description("search")]
Search,
[Description("tel")]
Telephone,
[Description("url")]
Url,
[Description("color")]
Color,
[Description("date")]
Date,
[Description("datetime-local")]
DateTimeLocal,
[Description("month")]
Month,
[Description("time")]
Time,
[Description("week")]
Week
namespace Connected;
}
public enum InputType
{
[Description("text")]
Text,
[Description("password")]
Password,
[Description("email")]
Email,
[Description("hidden")]
Hidden,
[Description("number")]
Number,
[Description("search")]
Search,
[Description("tel")]
Telephone,
[Description("url")]
Url,
[Description("color")]
Color,
[Description("date")]
Date,
[Description("datetime-local")]
DateTimeLocal,
[Description("month")]
Month,
[Description("time")]
Time,
[Description("week")]
Week
}

@ -4,62 +4,62 @@ namespace Connected;
public class Converter<TSourceType, TDestinationType>
{
public event EventHandler<string?>? ErrorOccured;
/// <summary>
/// The culture info being used for decimal points, date and time format, etc.
/// </summary>
public CultureInfo Culture { get; set; } = Converters.DefaultCulture;
public TDestinationType? Convert(TSourceType value)
{
try
{
return ConvertValue(value);
}
catch (Exception e)
{
TriggerError($"Conversion from {typeof(TSourceType).Name} to {typeof(TDestinationType).Name} failed: {e.Message}", e);
}
return default;
}
protected virtual TDestinationType? ConvertValue(TSourceType? value)
{
return default;
}
public TSourceType? ConvertBack(TDestinationType value)
{
try
{
return ConvertValueBack(value);
}
catch (Exception e)
{
TriggerError($"Conversion from {typeof(TDestinationType).Name} to {typeof(TSourceType).Name} failed: {e.Message}", e);
}
return default;
}
protected virtual TSourceType? ConvertValueBack(TDestinationType? value)
{
return default;
}
protected void TriggerError(string? msg, Exception? e = null)
{
ErrorOccured?.Invoke(this, msg);
OnErrorOccured(msg, e);
}
protected virtual void OnErrorOccured(string? msg, Exception? e)
{
}
public event EventHandler<string?>? ErrorOccured;
/// <summary>
/// The culture info being used for decimal points, date and time format, etc.
/// </summary>
public CultureInfo Culture { get; set; } = Converters.DefaultCulture;
public TDestinationType? Convert(TSourceType value)
{
try
{
return ConvertValue(value);
}
catch (Exception e)
{
TriggerError($"Conversion from {typeof(TSourceType).Name} to {typeof(TDestinationType).Name} failed: {e.Message}", e);
}
return default;
}
protected virtual TDestinationType? ConvertValue(TSourceType? value)
{
return default;
}
public TSourceType? ConvertBack(TDestinationType value)
{
try
{
return ConvertValueBack(value);
}
catch (Exception e)
{
TriggerError($"Conversion from {typeof(TDestinationType).Name} to {typeof(TSourceType).Name} failed: {e.Message}", e);
}
return default;
}
protected virtual TSourceType? ConvertValueBack(TDestinationType? value)
{
return default;
}
protected void TriggerError(string? msg, Exception? e = null)
{
ErrorOccured?.Invoke(this, msg);
OnErrorOccured(msg, e);
}
protected virtual void OnErrorOccured(string? msg, Exception? e)
{
}
}
/// <summary>
@ -67,32 +67,32 @@ public class Converter<TSourceType, TDestinationType>
/// </summary>
/// <typeparam name="TSourceType">The source type</typeparam>
/// <typeparam name="TDestinationType">The destination type</typeparam>
public class LambdaConverter<TSourceType, TDestinationType> :Converter<TSourceType, TDestinationType>
public class LambdaConverter<TSourceType, TDestinationType> : Converter<TSourceType, TDestinationType>
{
private readonly Func<TSourceType, TDestinationType?>? _convertFunction;
private readonly Func<TDestinationType, TSourceType?>? _convertBackFunction;
public LambdaConverter(Func<TSourceType, TDestinationType?>? convertFunction = null, Func<TDestinationType, TSourceType?>? convertBackFunction = null)
{
_convertFunction = convertFunction;
_convertBackFunction = convertBackFunction;
}
protected override TDestinationType? ConvertValue(TSourceType? value)
{
if (_convertFunction is null)
return base.ConvertValue(value);
return _convertFunction.Invoke(value);
}
protected override TSourceType? ConvertValueBack(TDestinationType? value)
{
if (_convertFunction is null)
return base.ConvertValueBack(value);
return _convertBackFunction.Invoke(value);
}
private readonly Func<TSourceType, TDestinationType?>? _convertFunction;
private readonly Func<TDestinationType, TSourceType?>? _convertBackFunction;
public LambdaConverter(Func<TSourceType, TDestinationType?>? convertFunction = null, Func<TDestinationType, TSourceType?>? convertBackFunction = null)
{
_convertFunction = convertFunction;
_convertBackFunction = convertBackFunction;
}
protected override TDestinationType? ConvertValue(TSourceType? value)
{
if (_convertFunction is null)
return base.ConvertValue(value);
return _convertFunction.Invoke(value);
}
protected override TSourceType? ConvertValueBack(TDestinationType? value)
{
if (_convertFunction is null)
return base.ConvertValueBack(value);
return _convertBackFunction.Invoke(value);
}
}
/// <summary>
@ -104,8 +104,8 @@ public class LambdaConverter<TSourceType, TDestinationType> :Converter<TSourceTy
public class ToStringConverter<T> : Converter<T, string>
{
/// <summary>
/// Custom Format to be applied on bidirectional way.
/// </summary>
public string? Format { get; set; } = null;
/// <summary>
/// Custom Format to be applied on bidirectional way.
/// </summary>
public string? Format { get; set; } = null;
}

@ -1,54 +1,8 @@
using Connected.Extensions;
using System.Globalization;
using System.Globalization;
namespace Connected
{
public static class Converters
{
public static CultureInfo DefaultCulture = CultureInfo.CurrentUICulture;
#region --> Date converters
public static LambdaConverter<DateTime, string> IsoDate
=> new(SetIsoDate, GetIsoDate);
private static DateTime GetIsoDate(string value)
{
if (DateTime.TryParse(value, out var dateTime))
return dateTime;
return DateTime.MinValue;
}
private static string SetIsoDate(DateTime value)
{
return value.ToIsoDateString();
}
public static LambdaConverter<DateTime?, string?> NullableIsoDate
=> new(SetNullableIsoDate, GetNullableIsoDate);
namespace Connected;
private static DateTime? GetNullableIsoDate(string? value)
{
if (DateTime.TryParse(value, out var dateTime))
return dateTime;
return null;
}
private static string SetNullableIsoDate(DateTime? value)
{
return value.ToIsoDateString();
}
public static DateConverter DateFormat(string format)
{
format ??= "yyyy-MM-dd";
return new DateConverter(format);
}
public static DateConverter DateFormat(string format, CultureInfo culture)
{
return new DateConverter(format) { Culture = culture };
}
#endregion
}
public static class Converters
{
public static CultureInfo DefaultCulture { get; set; } = CultureInfo.CurrentUICulture;
}

@ -1,106 +1,29 @@
using System;
namespace Connected;
namespace Connected
/// <summary>
/// A ready made DateTime? to string binding converter with configurable date format and culture
/// </summary>
public class NullableDateConverter : ToStringConverter<DateTime?>
{
public string DateFormat { get; set; }
/// <summary>
/// A ready made DateTime? to string binding converter with configurable date format and culture
/// </summary>
public class NullableDateConverter : ToStringConverter<DateTime?>
{
public string DateFormat { get; set; }
public NullableDateConverter(string format = "yyyy-MM-dd") => DateFormat = format;
public NullableDateConverter(string format = "yyyy-MM-dd")
{
DateFormat = format;
}
protected override string? ConvertValue(DateTime? value) => value?.ToString(DateFormat, Culture);
protected override string? ConvertValue(DateTime? value)
{
return OnSet(value);
}
protected override DateTime? ConvertValueBack(string? value)
{
return OnGet(value);
}
private DateTime? OnGet(string arg)
{
try
{
return DateTime.ParseExact(arg, DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return null;
}
}
private string OnSet(DateTime? arg)
{
if (arg == null)
return null;
try
{
return arg.Value.ToString(DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return null;
}
}
}
/// <summary>
/// A ready made DateTime to string binding converter with configurable date format and culture
/// </summary>
public class DateConverter : ToStringConverter<DateTime>
{
public string DateFormat { get; set; } = "yyyy-MM-dd";
public DateConverter(string format)
{
DateFormat = format;
}
protected override string? ConvertValue(DateTime value)
{
return OnSet(value);
}
protected override DateTime? ConvertValueBack(string? value) => DateTime.ParseExact(value, DateFormat, Culture);
}
protected override DateTime ConvertValueBack(string value)
{
return OnGet(value);
}
/// <summary>
/// A ready made DateTime to string binding converter with configurable date format and culture
/// </summary>
public class DateConverter : ToStringConverter<DateTime>
{
public string DateFormat { get; set; }
private DateTime OnGet(string arg)
{
try
{
return DateTime.ParseExact(arg, DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return default;
}
}
public DateConverter(string format = "yyyy-MM-dd") => DateFormat = format;
private string OnSet(DateTime arg)
{
try
{
return arg.ToString(DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return null;
}
}
}
protected override string? ConvertValue(DateTime value) => value.ToString(DateFormat, Culture);
}
protected override DateTime ConvertValueBack(string? value) => DateTime.ParseExact(value, DateFormat, Culture);
}

@ -1,323 +1,464 @@
using System;
using System.Globalization;
using System.Globalization;
namespace Connected
namespace Connected;
/// <summary>
/// A universal T to string binding converter
/// </summary>
public class DefaultConverter<T> : ToStringConverter<T>
{
protected override string? ConvertValue(T? value) => ConvertToString(value);
protected override T? ConvertValueBack(string? value) => ConvertFromString(value);
public string DefaultTimeSpanFormat { get; set; } = "c";
protected virtual T? ConvertFromString(string? value)
{
try
{
/*
* This is important, or otherwise all the TryParse down there might fail.
*/
if (string.IsNullOrEmpty(value))
{
return default;
}
/*
* String
*/
else if (typeof(T) == typeof(string))
{
return (T)(object)value;
}
/*
* Char
*/
else if (typeof(T) == typeof(char) || typeof(T) == typeof(char?))
{
return (T)(object)value[0];
}
/*
* Bool
*/
else if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?))
{
var lowerValue = value.ToLowerInvariant();
if (lowerValue is "true" or "on")
return (T)(object)true;
if (lowerValue is "false" or "off")
return (T)(object)false;
TriggerError("Not a valid boolean");
}
/*
* Sbyte
*/
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
{
if (sbyte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Byte
*/
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
{
if (byte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Short
*/
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
{
if (short.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Ushort
*/
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
{
if (ushort.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Int
*/
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
{
if (int.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Uint
*/
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
{
if (uint.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Long
*/
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
{
if (long.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Ulong
*/
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
{
if (ulong.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Float
*/
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
{
if (float.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Double
*/
else if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
{
if (double.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Decimal
*/
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
{
if (decimal.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
/*
* Guid
*/
else if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?))
{
if (Guid.TryParse(value, out var parsedValue))
return (T)(object)parsedValue;
/// <summary>
/// A universal T to string binding converter
/// </summary>
public class DefaultConverter<T> : ToStringConverter<T>
{
TriggerError("Not a valid GUID");
}
/*
* Enum
*/
else if (IsNullableEnum(typeof(T)))
{
var enum_type = Nullable.GetUnderlyingType(typeof(T));
public DefaultConverter()
{
}
if (Enum.TryParse(enum_type, value, out var parsedValue))
return (T)parsedValue;
protected override string? ConvertValue(T? value)
{
return ConvertToString(value);
}
TriggerError("Not a value of " + enum_type.Name);
}
else if (typeof(T).IsEnum)
{
if (Enum.TryParse(typeof(T), value, out var parsedValue))
return (T)parsedValue;
protected override T? ConvertValueBack(string? value)
{
return ConvertFromString(value);
}
TriggerError("Not a value of " + typeof(T).Name);
}
/*
* DateTime
*/
else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?))
{
try
{
return (T)(object)DateTime.ParseExact(value, Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
catch (FormatException)
{
TriggerError("Not a valid date time");
}
}
/*
* Timespan
*/
else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?))
{
try
{
return (T)(object)TimeSpan.ParseExact(value, Format ?? DefaultTimeSpanFormat, Culture);
}
catch (FormatException)
{
TriggerError("Not a valid time span");
}
}
else
{
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
TriggerError("Conversion error: " + e.Message);
}
public string DefaultTimeSpanFormat { get; set; } = "c";
return default;
}
protected virtual T ConvertFromString(string value)
{
try
{
// string
if (typeof(T) == typeof(string))
return (T)(object)value;
protected virtual string? ConvertToString(T? arg)
{
/*
* This catches all null values. No additional null checks necessary.
*/
if (arg is null)
return null;
// this is important, or otherwise all the TryParse down there might fail.
if (string.IsNullOrEmpty(value))
return default(T);
// char
else if (typeof(T) == typeof(char) || typeof(T) == typeof(char?))
{
return (T)(object)value[0];
}
// bool
else if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?))
{
var lowerValue = value.ToLowerInvariant();
if (lowerValue is "true" or "on")
return (T)(object)true;
if (lowerValue is "false" or "off")
return (T)(object)false;
TriggerError("Not a valid boolean");
}
// sbyte
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
{
if (sbyte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// byte
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
{
if (byte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// short
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
{
if (short.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// ushort
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
{
if (ushort.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// int
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
{
if (int.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// uint
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
{
if (uint.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// long
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
{
if (long.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// ulong
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
{
if (ulong.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// float
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
{
if (float.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// double
else if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
{
if (double.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// decimal
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
{
if (decimal.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid number");
}
// guid
else if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?))
{
if (Guid.TryParse(value, out var parsedValue))
return (T)(object)parsedValue;
TriggerError("Not a valid GUID");
}
// enum
else if (IsNullableEnum(typeof(T)))
{
var enum_type = Nullable.GetUnderlyingType(typeof(T));
if (Enum.TryParse(enum_type, value, out var parsedValue))
return (T)parsedValue;
TriggerError("Not a value of " + enum_type.Name);
}
else if (typeof(T).IsEnum)
{
if (Enum.TryParse(typeof(T), value, out var parsedValue))
return (T)parsedValue;
TriggerError("Not a value of " + typeof(T).Name);
}
// datetime
else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?))
{
try
{
return (T)(object)DateTime.ParseExact(value, Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
catch (FormatException)
{
TriggerError("Not a valid date time");
}
}
// timespan
else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?))
{
try
{
return (T)(object)TimeSpan.ParseExact(value, Format ?? DefaultTimeSpanFormat, Culture);
}
catch (FormatException)
{
TriggerError("Not a valid time span");
}
}
else
{
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
TriggerError("Conversion error: " + e.Message);
}
try
{
/*
* String
*/
if (typeof(T) == typeof(string))
{
return (string)(object)arg;
}
/*
* Char
*/
else if (typeof(T) == typeof(char))
{
return ((char)(object)arg).ToString(Culture);
}
else if (typeof(T) == typeof(char?))
{
return ((char?)(object)arg).Value.ToString(Culture);
}
/*
* Bool
*/
else if (typeof(T) == typeof(bool))
{
return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture);
}
else if (typeof(T) == typeof(bool?))
{
return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture);
}
/*
* Sbyte
*/
else if (typeof(T) == typeof(sbyte))
{
return ((sbyte)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(sbyte?))
{
return ((sbyte?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Byte
*/
else if (typeof(T) == typeof(byte))
{
return ((byte)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(byte?))
{
return ((byte?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Short
*/
else if (typeof(T) == typeof(short))
{
return ((short)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(short?))
{
return ((short?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Ushort
*/
else if (typeof(T) == typeof(ushort))
{
return ((ushort)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(ushort?))
{
return ((ushort?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Int
*/
else if (typeof(T) == typeof(int))
{
return ((int)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(int?))
{
return ((int?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Uint
*/
else if (typeof(T) == typeof(uint))
{
return ((uint)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(uint?))
{
return ((uint?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Long
*/
else if (typeof(T) == typeof(long))
{
return ((long)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(long?))
{
return ((long?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Ulong
*/
else if (typeof(T) == typeof(ulong))
{
return ((ulong)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(ulong?))
{
return ((ulong?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Float
*/
else if (typeof(T) == typeof(float))
{
return ((float)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(float?))
{
return ((float?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Double
*/
else if (typeof(T) == typeof(double))
{
return ((double)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(double?))
{
return ((double?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Decimal
*/
else if (typeof(T) == typeof(decimal))
{
return ((decimal)(object)arg).ToString(Format, Culture);
}
else if (typeof(T) == typeof(decimal?))
{
return ((decimal?)(object)arg).Value.ToString(Format, Culture);
}
/*
* Guid
*/
else if (typeof(T) == typeof(Guid))
{
var value = (Guid)(object)arg;
return value.ToString();
}
else if (typeof(T) == typeof(Guid?))
{
var value = (Guid?)(object)arg;
return value.Value.ToString();
}
/*
* Enum
*/
else if (IsNullableEnum(typeof(T)))
{
var value = (Enum)(object)arg;
return value.ToString();
}
else if (typeof(T).IsEnum)
{
var value = (Enum)(object)arg;
return value.ToString();
}
/*
* DateTime
*/
else if (typeof(T) == typeof(DateTime))
{
var value = (DateTime)(object)arg;
return value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
else if (typeof(T) == typeof(DateTime?))
{
var value = (DateTime?)(object)arg;
return value.Value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
/*
* Timespan
*/
else if (typeof(T) == typeof(TimeSpan))
{
var value = (TimeSpan)(object)arg;
return value.ToString(Format ?? DefaultTimeSpanFormat, Culture);
}
else if (typeof(T) == typeof(TimeSpan?))
{
var value = (TimeSpan?)(object)arg;
return value.Value.ToString(Format ?? DefaultTimeSpanFormat, Culture);
}
return default(T);
}
return arg.ToString();
}
catch (FormatException e)
{
TriggerError("Conversion error: " + e.Message);
return null;
}
}
protected virtual string ConvertToString(T arg)
{
if (arg == null)
return null; // <-- this catches all nullable values which are null. no nullchecks necessary below!
try
{
// string
if (typeof(T) == typeof(string))
return (string)(object)arg;
// char
if (typeof(T) == typeof(char))
return ((char)(object)arg).ToString(Culture);
if (typeof(T) == typeof(char?))
return ((char?)(object)arg).Value.ToString(Culture);
// bool
if (typeof(T) == typeof(bool))
return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture);
if (typeof(T) == typeof(bool?))
return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture);
// sbyte
if (typeof(T) == typeof(sbyte))
return ((sbyte)(object)arg).ToString(Format, Culture);
if (typeof(T) == typeof(sbyte?))
return ((sbyte?)(object)arg).Value.ToString(Format, Culture);
// byte
if (typeof(T) == typeof(byte))
return ((byte)(object)arg).ToString(Format, Culture);
if (typeof(T) == typeof(byte?))
return ((byte?)(object)arg).Value.ToString(Format, Culture);
// short
if (typeof(T) == typeof(short))
return ((short)(object)arg).ToString(Format, Culture);
if (typeof(T) == typeof(short?))
return ((short?)(object)arg).Value.ToString(Format, Culture);
// ushort
if (typeof(T) == typeof(ushort))
return ((ushort)(object)arg).ToString(Format, Culture);
if (typeof(T) == typeof(ushort?))
return ((ushort?)(object)arg).Value.ToString(Format, Culture);
// int
else if (typeof(T) == typeof(int))
return ((int)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(int?))
return ((int?)(object)arg).Value.ToString(Format, Culture);
// uint
else if (typeof(T) == typeof(uint))
return ((uint)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(uint?))
return ((uint?)(object)arg).Value.ToString(Format, Culture);
// long
else if (typeof(T) == typeof(long))
return ((long)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(long?))
return ((long?)(object)arg).Value.ToString(Format, Culture);
// ulong
else if (typeof(T) == typeof(ulong))
return ((ulong)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(ulong?))
return ((ulong?)(object)arg).Value.ToString(Format, Culture);
// float
else if (typeof(T) == typeof(float))
return ((float)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(float?))
return ((float?)(object)arg).Value.ToString(Format, Culture);
// double
else if (typeof(T) == typeof(double))
return ((double)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(double?))
return ((double?)(object)arg).Value.ToString(Format, Culture);
// decimal
else if (typeof(T) == typeof(decimal))
return ((decimal)(object)arg).ToString(Format, Culture);
else if (typeof(T) == typeof(decimal?))
return ((decimal?)(object)arg).Value.ToString(Format, Culture);
// guid
else if (typeof(T) == typeof(Guid))
{
var value = (Guid)(object)arg;
return value.ToString();
}
else if (typeof(T) == typeof(Guid?))
{
var value = (Guid?)(object)arg;
return value.Value.ToString();
}
// enum
else if (IsNullableEnum(typeof(T)))
{
var value = (Enum)(object)arg;
return value.ToString();
}
else if (typeof(T).IsEnum)
{
var value = (Enum)(object)arg;
return value.ToString();
}
// datetime
else if (typeof(T) == typeof(DateTime))
{
var value = (DateTime)(object)arg;
return value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
else if (typeof(T) == typeof(DateTime?))
{
var value = (DateTime?)(object)arg;
return value.Value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
}
// timespan
else if (typeof(T) == typeof(TimeSpan))
{
var value = (TimeSpan)(object)arg;
return value.ToString(Format ?? DefaultTimeSpanFormat, Culture);
}
else if (typeof(T) == typeof(TimeSpan?))
{
var value = (TimeSpan?)(object)arg;
return value.Value.ToString(Format ?? DefaultTimeSpanFormat, Culture);
}
return arg.ToString();
}
catch (FormatException e)
{
TriggerError("Conversion error: " + e.Message);
return null;
}
}
public static bool IsNullableEnum(Type t)
{
var u = Nullable.GetUnderlyingType(t);
public static bool IsNullableEnum(Type t)
{
var u = Nullable.GetUnderlyingType(t);
return (u != null) && u.IsEnum;
}
}
return (u is not null) && u.IsEnum;
}
}

@ -0,0 +1,17 @@
using Connected.Extensions;
namespace Connected;
public class IsoDateConverter : Converter<DateTime, string>
{
protected override string ConvertValue(DateTime value) => value.ToIsoDateString();
protected override DateTime ConvertValueBack(string? value) => DateTime.TryParse(value, out var dateTime) ? dateTime : DateTime.MinValue;
}
public class NullableIsoDateConverter : Converter<DateTime?, string?>
{
protected override string? ConvertValue(DateTime? value) => value?.ToIsoDateString();
protected override DateTime? ConvertValueBack(string? value) => DateTime.TryParse(value, out var dateTime) ? dateTime : null;
}

@ -1,293 +1,390 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace Connected
{
/// <summary>
/// A universal T to double binding converter
///
/// Note: currently not in use. Should we ever use it, remove
/// the [ExcludeFromCodeCoverage] attribute
/// </summary>
[ExcludeFromCodeCoverage]
public class NumericConverter<T> : Converter<T, double>
{
namespace Connected;
public NumericConverter()
{
}
/// <summary>
/// A universal T to double binding converter
///
/// Note: currently not in use. Should we ever use it, remove
/// the [ExcludeFromCodeCoverage] attribute
/// </summary>
[ExcludeFromCodeCoverage]
public class NumericConverter<T> : Converter<T, double>
{
protected override double ConvertValue(T? value)
protected override double ConvertValue(T? value)
{
if (value is null)
{
return OnSet(value);
return double.NaN;
}
protected override T? ConvertValueBack(double value)
/*
* Double
*/
else if (typeof(T) == typeof(double))
{
return OnGet(value);
return (double)(object)value;
}
else if (typeof(T) == typeof(double?))
{
return ((double?)(object)value).Value;
}
/*
* String
*/
else if (typeof(T) == typeof(string))
{
return double.Parse((string)(object)value, NumberStyles.Any, Culture);
}
/*
* SByte
*/
else if (typeof(T) == typeof(sbyte))
{
return System.Convert.ToDouble((sbyte)(object)value);
}
else if (typeof(T) == typeof(sbyte?))
{
return System.Convert.ToDouble(((sbyte?)(object)value).Value);
}
/*
* Byte
*/
else if (typeof(T) == typeof(byte))
{
return System.Convert.ToDouble((byte)(object)value);
}
else if (typeof(T) == typeof(byte?))
{
return System.Convert.ToDouble(((byte?)(object)value).Value);
}
/*
* Short
*/
else if (typeof(T) == typeof(short))
{
return System.Convert.ToDouble((short)(object)value);
}
else if (typeof(T) == typeof(short?))
{
return System.Convert.ToDouble(((short?)(object)value).Value);
}
/*
* Ushort
*/
else if (typeof(T) == typeof(ushort))
{
return System.Convert.ToDouble((ushort)(object)value);
}
else if (typeof(T) == typeof(ushort?))
{
return System.Convert.ToDouble(((ushort?)(object)value).Value);
}
/*
* Int
*/
else if (typeof(T) == typeof(int))
{
return System.Convert.ToDouble((int)(object)value);
}
else if (typeof(T) == typeof(int?))
{
return System.Convert.ToDouble(((int?)(object)value).Value);
}
/*
* Uint
*/
else if (typeof(T) == typeof(uint))
{
return System.Convert.ToDouble((uint)(object)value);
}
else if (typeof(T) == typeof(uint?))
{
return System.Convert.ToDouble(((uint?)(object)value).Value);
}
/*
* Long
*/
else if (typeof(T) == typeof(long))
{
return System.Convert.ToDouble((long)(object)value);
}
else if (typeof(T) == typeof(long?))
{
return System.Convert.ToDouble(((long?)(object)value).Value);
}
/*
* Ulong
*/
else if (typeof(T) == typeof(ulong))
{
return System.Convert.ToDouble((ulong)(object)value);
}
else if (typeof(T) == typeof(ulong?))
{
return System.Convert.ToDouble(((ulong?)(object)value).Value);
}
/*
* Float
*/
else if (typeof(T) == typeof(float))
{
return System.Convert.ToDouble((float)(object)value);
}
else if (typeof(T) == typeof(float?))
{
return System.Convert.ToDouble(((float?)(object)value).Value);
}
/*
* Deimal
*/
else if (typeof(T) == typeof(decimal))
{
return System.Convert.ToDouble((decimal)(object)value);
}
else if (typeof(T) == typeof(decimal?))
{
return System.Convert.ToDouble(((decimal?)(object)value).Value);
}
else
{
TriggerError("Unable to convert to double from type " + typeof(T).Name);
return double.NaN;
}
}
private T OnGet(double value)
{
try
{
// double
if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
return (T)(object)value;
// string
else if (typeof(T) == typeof(string))
return (T)(object)value.ToString(Culture);
// sbyte
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
return (T)(object)System.Convert.ToSByte(value);
// byte
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
return (T)(object)System.Convert.ToByte(value);
// short
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
return (T)(object)System.Convert.ToInt16(value);
// ushort
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
return (T)(object)System.Convert.ToUInt16(value);
// int
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
return (T)(object)System.Convert.ToInt32(value);
// uint
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
return (T)(object)System.Convert.ToUInt32(value);
// long
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
return (T)(object)System.Convert.ToInt64(value);
// ulong
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
return (T)(object)System.Convert.ToUInt64(value);
// float
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
return (T)(object)System.Convert.ToSingle(value);
// decimal
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
return (T)(object)System.Convert.ToDecimal(value);
else
{
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
TriggerError("Conversion error: " + e.Message);
return default(T);
}
return default(T);
}
private double OnSet(T arg)
{
if (arg == null)
return double.NaN; // <-- this catches all nullable values which are null. no nullchecks necessary below!
try
{
// double
if (typeof(T) == typeof(double))
return (double)(object)arg;
else if (typeof(T) == typeof(double?))
return ((double?)(object)arg).Value;
// string
if (typeof(T) == typeof(string))
return double.Parse((string)(object)arg, NumberStyles.Any, Culture);
// sbyte
if (typeof(T) == typeof(sbyte))
return System.Convert.ToDouble((sbyte)(object)arg);
if (typeof(T) == typeof(sbyte?))
return System.Convert.ToDouble(((sbyte?)(object)arg).Value);
// byte
if (typeof(T) == typeof(byte))
return System.Convert.ToDouble((byte)(object)arg);
if (typeof(T) == typeof(byte?))
return System.Convert.ToDouble(((byte?)(object)arg).Value);
// short
if (typeof(T) == typeof(short))
return System.Convert.ToDouble((short)(object)arg);
if (typeof(T) == typeof(short?))
return System.Convert.ToDouble(((short?)(object)arg).Value);
// ushort
if (typeof(T) == typeof(ushort))
return System.Convert.ToDouble((ushort)(object)arg);
if (typeof(T) == typeof(ushort?))
return System.Convert.ToDouble(((ushort?)(object)arg).Value);
// int
else if (typeof(T) == typeof(int))
return System.Convert.ToDouble((int)(object)arg);
else if (typeof(T) == typeof(int?))
return System.Convert.ToDouble(((int?)(object)arg).Value);
// uint
else if (typeof(T) == typeof(uint))
return System.Convert.ToDouble((uint)(object)arg);
else if (typeof(T) == typeof(uint?))
return System.Convert.ToDouble(((uint?)(object)arg).Value);
// long
else if (typeof(T) == typeof(long))
return System.Convert.ToDouble((long)(object)arg);
else if (typeof(T) == typeof(long?))
return System.Convert.ToDouble(((long?)(object)arg).Value);
// ulong
else if (typeof(T) == typeof(ulong))
return System.Convert.ToDouble((ulong)(object)arg);
else if (typeof(T) == typeof(ulong?))
return System.Convert.ToDouble(((ulong?)(object)arg).Value);
// float
else if (typeof(T) == typeof(float))
return System.Convert.ToDouble((float)(object)arg);
else if (typeof(T) == typeof(float?))
return System.Convert.ToDouble(((float?)(object)arg).Value);
// decimal
else if (typeof(T) == typeof(decimal))
return System.Convert.ToDouble((decimal)(object)arg);
else if (typeof(T) == typeof(decimal?))
return System.Convert.ToDouble(((decimal?)(object)arg).Value);
else
{
TriggerError("Unable to convert to double from type " + typeof(T).Name);
return double.NaN;
}
}
catch (FormatException e)
{
TriggerError("Conversion error: " + e.Message);
return double.NaN;
}
}
protected override T? ConvertValueBack(double value)
{
/*
* Double
*/
if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
{
return (T)(object)value;
}
/*
* String
*/
else if (typeof(T) == typeof(string))
{
return (T)(object)value.ToString(Culture);
}
/*
* Sbyte
*/
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
{
return (T)(object)System.Convert.ToSByte(value);
}
/*
* Byte
*/
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
{
return (T)(object)System.Convert.ToByte(value);
}
/*
* Short
*/
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
{
return (T)(object)System.Convert.ToInt16(value);
}
/*
* Ushort
*/
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
{
return (T)(object)System.Convert.ToUInt16(value);
}
/*
* Int
*/
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
{
return (T)(object)System.Convert.ToInt32(value);
}
/*
* Uint
*/
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
{
return (T)(object)System.Convert.ToUInt32(value);
}
/*
* Long
*/
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
{
return (T)(object)System.Convert.ToInt64(value);
}
/*
* Ulong
*/
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
{
return (T)(object)System.Convert.ToUInt64(value);
}
/*
* Float
*/
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
{
return (T)(object)System.Convert.ToSingle(value);
}
/*
* Decimal
*/
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
{
return (T)(object)System.Convert.ToDecimal(value);
}
else
{
TriggerError($"Conversion to type {typeof(T)} not implemented");
return default;
}
}
#region Floating Point comparison
#region --> Floating Point comparison
private const double MinNormal = 2.2250738585072014E-308d;
const double MinNormal = 2.2250738585072014E-308d;
public static bool AreEqual(double a, double b, double epsilon = MinNormal)
{
/*
* Copyright (c) Michael Borgwardt
*/
var absA = Math.Abs(a);
var absB = Math.Abs(b);
var diff = Math.Abs(a - b);
/*
* Shortcut, handles infinities
*/
if (a.Equals(b))
{
return true;
}
else if (a == 0 || b == 0 || absA + absB < MinNormal)
{
/*
* a or b is zero or both are extremely close to it
* relative error is less meaningful here
*/
return diff < epsilon * MinNormal;
}
else
{
/*
* Use relative error
*/
return diff / (absA + absB) < epsilon;
}
}
public static bool AreEqual(double a, double b, double epsilon = MinNormal)
{
// Copyright (c) Michael Borgwardt
var absA = Math.Abs(a);
var absB = Math.Abs(b);
var diff = Math.Abs(a - b);
#endregion
}
if (a.Equals(b))
{ // shortcut, handles infinities
return true;
}
else if (a == 0 || b == 0 || absA + absB < MinNormal)
{
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * MinNormal);
}
else
{ // use relative error
return diff / (absA + absB) < epsilon;
}
}
[ExcludeFromCodeCoverage]
internal static class Number
{
public static T? To<T>(double d)
{
if (typeof(T) == typeof(sbyte) && d >= sbyte.MinValue && sbyte.MaxValue >= d)
return (T)(object)Convert.ToSByte(d);
if (typeof(T) == typeof(byte) && d >= byte.MinValue && byte.MaxValue >= d)
return (T)(object)Convert.ToByte(d);
if (typeof(T) == typeof(short) && d >= short.MinValue && short.MaxValue >= d)
return (T)(object)Convert.ToInt16(d);
if (typeof(T) == typeof(ushort) && d >= ushort.MinValue && ushort.MaxValue >= d)
return (T)(object)Convert.ToUInt16(d);
if (typeof(T) == typeof(int) && d >= int.MinValue && int.MaxValue >= d)
return (T)(object)Convert.ToInt32(d);
if (typeof(T) == typeof(uint) && d >= uint.MinValue && uint.MaxValue >= d)
return (T)(object)Convert.ToUInt32(d);
if (typeof(T) == typeof(long) && d >= long.MinValue && long.MaxValue >= d)
return (T)(object)Convert.ToInt64(d);
if (typeof(T) == typeof(ulong) && d >= ulong.MinValue && ulong.MaxValue >= d)
return (T)(object)Convert.ToUInt64(d);
if (typeof(T) == typeof(float) && d >= float.MinValue && float.MaxValue >= d)
return (T)(object)Convert.ToSingle(d);
if (typeof(T) == typeof(double) && d >= double.MinValue && double.MaxValue >= d)
return (T)(object)Convert.ToDouble(d);
if (typeof(T) == typeof(decimal) && (decimal)d >= decimal.MinValue && decimal.MaxValue >= (decimal)d)
return (T)(object)Convert.ToDecimal(d);
if (typeof(T) == typeof(sbyte?) && d >= sbyte.MinValue && sbyte.MaxValue >= d)
return (T)(object)Convert.ToSByte(d);
if (typeof(T) == typeof(byte?) && d >= byte.MinValue && byte.MaxValue >= d)
return (T)(object)Convert.ToByte(d);
if (typeof(T) == typeof(short?) && d >= short.MinValue && short.MaxValue >= d)
return (T)(object)Convert.ToInt16(d);
if (typeof(T) == typeof(ushort?) && d >= ushort.MinValue && ushort.MaxValue >= d)
return (T)(object)Convert.ToUInt16(d);
if (typeof(T) == typeof(int?) && d >= int.MinValue && int.MaxValue >= d)
return (T)(object)Convert.ToInt32(d);
if (typeof(T) == typeof(uint?) && d >= uint.MinValue && uint.MaxValue >= d)
return (T)(object)Convert.ToUInt32(d);
if (typeof(T) == typeof(long?) && d >= long.MinValue && long.MaxValue >= d)
return (T)(object)Convert.ToInt64(d);
if (typeof(T) == typeof(ulong?) && d >= ulong.MinValue && ulong.MaxValue >= d)
return (T)(object)Convert.ToUInt64(d);
if (typeof(T) == typeof(float?) && d >= float.MinValue && float.MaxValue >= d)
return (T)(object)Convert.ToSingle(d);
if (typeof(T) == typeof(double?) && d >= double.MinValue && double.MaxValue >= d)
return (T)(object)Convert.ToDouble(d);
if (typeof(T) == typeof(decimal?) && (decimal)d >= decimal.MinValue && decimal.MaxValue >= (decimal)d)
return (T)(object)Convert.ToDecimal(d);
#endregion
}
return default;
}
public static double From<T>(T v)
{
if (typeof(T) == typeof(sbyte))
return Convert.ToDouble((sbyte)(object)v);
if (typeof(T) == typeof(byte))
return Convert.ToDouble((byte)(object)v);
if (typeof(T) == typeof(short))
return Convert.ToDouble((short)(object)v);
if (typeof(T) == typeof(ushort))
return Convert.ToDouble((ushort)(object)v);
if (typeof(T) == typeof(int))
return Convert.ToDouble((int)(object)v);
if (typeof(T) == typeof(uint))
return Convert.ToDouble((uint)(object)v);
if (typeof(T) == typeof(long))
return Convert.ToDouble((long)(object)v);
if (typeof(T) == typeof(ulong))
return Convert.ToDouble((ulong)(object)v);
if (typeof(T) == typeof(float))
return Convert.ToDouble((float)(object)v);
if (typeof(T) == typeof(double))
return Convert.ToDouble((double)(object)v);
if (typeof(T) == typeof(decimal))
return Convert.ToDouble((decimal)(object)v);
if (typeof(T) == typeof(sbyte?))
return Convert.ToDouble((sbyte?)(object)v);
if (typeof(T) == typeof(byte?))
return Convert.ToDouble((byte?)(object)v);
if (typeof(T) == typeof(short?))
return Convert.ToDouble((short?)(object)v);
if (typeof(T) == typeof(ushort?))
return Convert.ToDouble((ushort?)(object)v);
if (typeof(T) == typeof(int?))
return Convert.ToDouble((int?)(object)v);
if (typeof(T) == typeof(uint?))
return Convert.ToDouble((uint?)(object)v);
if (typeof(T) == typeof(long?))
return Convert.ToDouble((long?)(object)v);
if (typeof(T) == typeof(ulong?))
return Convert.ToDouble((ulong?)(object)v);
if (typeof(T) == typeof(float?))
return Convert.ToDouble((float?)(object)v);
if (typeof(T) == typeof(double?))
return Convert.ToDouble((double?)(object)v);
if (typeof(T) == typeof(decimal?))
return Convert.ToDouble((decimal?)(object)v);
[ExcludeFromCodeCoverage]
internal static class Num
{
public static T To<T>(double d)
{
if (typeof(T) == typeof(sbyte) && d >= sbyte.MinValue && sbyte.MaxValue >= d)
return (T)(object)Convert.ToSByte(d);
if (typeof(T) == typeof(byte) && d >= byte.MinValue && byte.MaxValue >= d)
return (T)(object)Convert.ToByte(d);
if (typeof(T) == typeof(short) && d >= short.MinValue && short.MaxValue >= d)
return (T)(object)Convert.ToInt16(d);
if (typeof(T) == typeof(ushort) && d >= ushort.MinValue && ushort.MaxValue >= d)
return (T)(object)Convert.ToUInt16(d);
if (typeof(T) == typeof(int) && d >= int.MinValue && int.MaxValue >= d)
return (T)(object)Convert.ToInt32(d);
if (typeof(T) == typeof(uint) && d >= uint.MinValue && uint.MaxValue >= d)
return (T)(object)Convert.ToUInt32(d);
if (typeof(T) == typeof(long) && d >= long.MinValue && long.MaxValue >= d)
return (T)(object)Convert.ToInt64(d);
if (typeof(T) == typeof(ulong) && d >= ulong.MinValue && ulong.MaxValue >= d)
return (T)(object)Convert.ToUInt64(d);
if (typeof(T) == typeof(float) && d >= float.MinValue && float.MaxValue >= d)
return (T)(object)Convert.ToSingle(d);
if (typeof(T) == typeof(double) && d >= double.MinValue && double.MaxValue >= d)
return (T)(object)Convert.ToDouble(d);
if (typeof(T) == typeof(decimal) && (decimal)d >= decimal.MinValue && decimal.MaxValue >= (decimal)d)
return (T)(object)Convert.ToDecimal(d);
if (typeof(T) == typeof(sbyte?) && d >= sbyte.MinValue && sbyte.MaxValue >= d)
return (T)(object)Convert.ToSByte(d);
if (typeof(T) == typeof(byte?) && d >= byte.MinValue && byte.MaxValue >= d)
return (T)(object)Convert.ToByte(d);
if (typeof(T) == typeof(short?) && d >= short.MinValue && short.MaxValue >= d)
return (T)(object)Convert.ToInt16(d);
if (typeof(T) == typeof(ushort?) && d >= ushort.MinValue && ushort.MaxValue >= d)
return (T)(object)Convert.ToUInt16(d);
if (typeof(T) == typeof(int?) && d >= int.MinValue && int.MaxValue >= d)
return (T)(object)Convert.ToInt32(d);
if (typeof(T) == typeof(uint?) && d >= uint.MinValue && uint.MaxValue >= d)
return (T)(object)Convert.ToUInt32(d);
if (typeof(T) == typeof(long?) && d >= long.MinValue && long.MaxValue >= d)
return (T)(object)Convert.ToInt64(d);
if (typeof(T) == typeof(ulong?) && d >= ulong.MinValue && ulong.MaxValue >= d)
return (T)(object)Convert.ToUInt64(d);
if (typeof(T) == typeof(float?) && d >= float.MinValue && float.MaxValue >= d)
return (T)(object)Convert.ToSingle(d);
if (typeof(T) == typeof(double?) && d >= double.MinValue && double.MaxValue >= d)
return (T)(object)Convert.ToDouble(d);
if (typeof(T) == typeof(decimal?) && (decimal)d >= decimal.MinValue && decimal.MaxValue >= (decimal)d)
return (T)(object)Convert.ToDecimal(d);
return default;
}
public static double From<T>(T v)
{
if (typeof(T) == typeof(sbyte))
return Convert.ToDouble((sbyte)(object)v);
if (typeof(T) == typeof(byte))
return Convert.ToDouble((byte)(object)v);
if (typeof(T) == typeof(short))
return Convert.ToDouble((short)(object)v);
if (typeof(T) == typeof(ushort))
return Convert.ToDouble((ushort)(object)v);
if (typeof(T) == typeof(int))
return Convert.ToDouble((int)(object)v);
if (typeof(T) == typeof(uint))
return Convert.ToDouble((uint)(object)v);
if (typeof(T) == typeof(long))
return Convert.ToDouble((long)(object)v);
if (typeof(T) == typeof(ulong))
return Convert.ToDouble((ulong)(object)v);
if (typeof(T) == typeof(float))
return Convert.ToDouble((float)(object)v);
if (typeof(T) == typeof(double))
return Convert.ToDouble((double)(object)v);
if (typeof(T) == typeof(decimal))
return Convert.ToDouble((decimal)(object)v);
if (typeof(T) == typeof(sbyte?))
return Convert.ToDouble((sbyte?)(object)v);
if (typeof(T) == typeof(byte?))
return Convert.ToDouble((byte?)(object)v);
if (typeof(T) == typeof(short?))
return Convert.ToDouble((short?)(object)v);
if (typeof(T) == typeof(ushort?))
return Convert.ToDouble((ushort?)(object)v);
if (typeof(T) == typeof(int?))
return Convert.ToDouble((int?)(object)v);
if (typeof(T) == typeof(uint?))
return Convert.ToDouble((uint?)(object)v);
if (typeof(T) == typeof(long?))
return Convert.ToDouble((long?)(object)v);
if (typeof(T) == typeof(ulong?))
return Convert.ToDouble((ulong?)(object)v);
if (typeof(T) == typeof(float?))
return Convert.ToDouble((float?)(object)v);
if (typeof(T) == typeof(double?))
return Convert.ToDouble((double?)(object)v);
if (typeof(T) == typeof(decimal?))
return Convert.ToDouble((decimal?)(object)v);
return default;
}
}
return default;
}
}

@ -1,65 +1,37 @@
using Connected.Components;
namespace Connected;
namespace Connected.Utilities.BindingConverters;
public class RangeConverter<T> : ToStringConverter<Range<T>>
{
readonly DefaultConverter<T> _converter;
private readonly DefaultConverter<T> _converter;
public RangeConverter()
{
_converter = new DefaultConverter<T>();
}
public RangeConverter() => _converter = new DefaultConverter<T>();
protected override string? ConvertValue(Range<T>? value)
{
return OnSet(value);
}
protected override string? ConvertValue(Range<T>? value) => value is not null
? Join(_converter.Convert(value.Start), _converter.Convert(value.End))
: string.Empty;
protected override Range<T>? ConvertValueBack(string? value)
{
return OnGet(value);
}
private Range<T> OnGet(string value)
{
if (!Split(value, out var valueStart, out var valueEnd))
return null;
return new Range<T>(_converter.ConvertBack(valueStart), _converter.ConvertBack(valueEnd));
return !Split(value, out var valueStart, out var valueEnd)
? null
: new Range<T>(_converter.ConvertBack(valueStart), _converter.ConvertBack(valueEnd));
}
private string OnSet(Range<T> arg)
{
if (arg == null)
return string.Empty;
return Join(_converter.Convert(arg.Start), _converter.Convert(arg.End));
}
public static string Join(string valueStart, string valueEnd)
{
if (string.IsNullOrEmpty(valueStart) && string.IsNullOrEmpty(valueEnd))
return string.Empty;
return $"[{valueStart};{valueEnd}]";
}
public static string Join(string? valueStart, string? valueEnd) => string.IsNullOrEmpty(valueStart) && string.IsNullOrEmpty(valueEnd) ? string.Empty : $"[{valueStart};{valueEnd}]";
public static bool Split(string value, out string valueStart, out string valueEnd)
public static bool Split(string? value, out string? valueStart, out string? valueEnd)
{
valueStart = valueEnd = string.Empty;
if (string.IsNullOrEmpty(value) || value[0] != '[' || value[^1] != ']')
{
return false;
}
var idx = value.IndexOf(';');
if (idx < 1)
{
return false;
}
valueStart = value[1..idx];
valueEnd = value[(idx + 1)..^1];

Loading…
Cancel
Save