[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; _toStringFunc = value;
Converter = new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null); SetConverter(new LambdaConverter<T, string>(_toStringFunc ?? (x => x?.ToString()), null));
} }
} }
/// <summary> /// <summary>

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

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

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

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

@ -8,6 +8,7 @@ using System.Globalization;
using Connected.Annotations; using Connected.Annotations;
using Connected.Services; using Connected.Services;
using Connected.Utilities; using Connected.Utilities;
using Connected.Utilities.BindingConverters;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web; 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); return (T)(object)Convert.ToInt64(FromInt64(Value) + FromInt64(Step) * factor);
if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
return (T)(object)Convert.ToUInt64(FromUInt64(Value) + FromUInt64(Step) * factor); 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> /// <summary>

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

@ -293,7 +293,7 @@ public partial class Select<T> : InputBase<T>, ISelect, IShadowSelect
if (_toStringFunc == value) if (_toStringFunc == value)
return; return;
_toStringFunc = value; _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.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Connected.Annotations; using Connected.Annotations;
@ -20,7 +19,7 @@ public partial class TimePicker : Picker<TimeSpan?>
{ {
_timeFormat = format24Hours; _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; AdornmentIcon = Icons.Material.Filled.AccessTime;
AdornmentAriaLabel = "Open Time Picker"; AdornmentAriaLabel = "Open Time Picker";

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

@ -1,54 +1,8 @@
using Connected.Extensions; using System.Globalization;
using System.Globalization;
namespace Connected 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);
private static DateTime? GetNullableIsoDate(string? value) public static class Converters
{ {
if (DateTime.TryParse(value, out var dateTime)) public static CultureInfo DefaultCulture { get; set; } = CultureInfo.CurrentUICulture;
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
}
} }

@ -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> public NullableDateConverter(string format = "yyyy-MM-dd") => DateFormat = format;
/// 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") protected override string? ConvertValue(DateTime? value) => value?.ToString(DateFormat, Culture);
{
DateFormat = format;
}
protected override string? ConvertValue(DateTime? value) protected override DateTime? ConvertValueBack(string? value) => DateTime.ParseExact(value, DateFormat, Culture);
{ }
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) /// <summary>
{ /// A ready made DateTime to string binding converter with configurable date format and culture
return OnGet(value); /// </summary>
} public class DateConverter : ToStringConverter<DateTime>
{
public string DateFormat { get; set; }
private DateTime OnGet(string arg) public DateConverter(string format = "yyyy-MM-dd") => DateFormat = format;
{
try
{
return DateTime.ParseExact(arg, DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return default;
}
}
private string OnSet(DateTime arg) protected override string? ConvertValue(DateTime value) => value.ToString(DateFormat, Culture);
{
try
{
return arg.ToString(DateFormat, Culture);
}
catch (FormatException e)
{
TriggerError(e.Message);
return null;
}
}
}
} 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> TriggerError("Not a valid GUID");
/// A universal T to string binding converter }
/// </summary> /*
public class DefaultConverter<T> : ToStringConverter<T> * 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) TriggerError("Not a value of " + enum_type.Name);
{ }
return ConvertToString(value); else if (typeof(T).IsEnum)
} {
if (Enum.TryParse(typeof(T), value, out var parsedValue))
return (T)parsedValue;
protected override T? ConvertValueBack(string? value) TriggerError("Not a value of " + typeof(T).Name);
{ }
return ConvertFromString(value); /*
} * 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) protected virtual string? ConvertToString(T? arg)
{ {
try /*
{ * This catches all null values. No additional null checks necessary.
// string */
if (typeof(T) == typeof(string)) if (arg is null)
return (T)(object)value; return null;
// this is important, or otherwise all the TryParse down there might fail. try
if (string.IsNullOrEmpty(value)) {
return default(T); /*
// char * String
else if (typeof(T) == typeof(char) || typeof(T) == typeof(char?)) */
{ if (typeof(T) == typeof(string))
return (T)(object)value[0]; {
} return (string)(object)arg;
// bool }
else if (typeof(T) == typeof(bool) || typeof(T) == typeof(bool?)) /*
{ * Char
var lowerValue = value.ToLowerInvariant(); */
if (lowerValue is "true" or "on") else if (typeof(T) == typeof(char))
return (T)(object)true; {
if (lowerValue is "false" or "off") return ((char)(object)arg).ToString(Culture);
return (T)(object)false; }
TriggerError("Not a valid boolean"); else if (typeof(T) == typeof(char?))
} {
// sbyte return ((char?)(object)arg).Value.ToString(Culture);
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?)) }
{ /*
if (sbyte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) * Bool
return (T)(object)parsedValue; */
TriggerError("Not a valid number"); else if (typeof(T) == typeof(bool))
} {
// byte return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture);
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?)) }
{ else if (typeof(T) == typeof(bool?))
if (byte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) {
return (T)(object)parsedValue; return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture);
TriggerError("Not a valid number"); }
} /*
// short * Sbyte
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?)) */
{ else if (typeof(T) == typeof(sbyte))
if (short.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) {
return (T)(object)parsedValue; return ((sbyte)(object)arg).ToString(Format, Culture);
TriggerError("Not a valid number"); }
} else if (typeof(T) == typeof(sbyte?))
// ushort {
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?)) return ((sbyte?)(object)arg).Value.ToString(Format, Culture);
{ }
if (ushort.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) /*
return (T)(object)parsedValue; * Byte
TriggerError("Not a valid number"); */
} else if (typeof(T) == typeof(byte))
// int {
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?)) return ((byte)(object)arg).ToString(Format, Culture);
{ }
if (int.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) else if (typeof(T) == typeof(byte?))
return (T)(object)parsedValue; {
TriggerError("Not a valid number"); return ((byte?)(object)arg).Value.ToString(Format, Culture);
} }
// uint /*
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?)) * Short
{ */
if (uint.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) else if (typeof(T) == typeof(short))
return (T)(object)parsedValue; {
TriggerError("Not a valid number"); return ((short)(object)arg).ToString(Format, Culture);
} }
// long else if (typeof(T) == typeof(short?))
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?)) {
{ return ((short?)(object)arg).Value.ToString(Format, Culture);
if (long.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) }
return (T)(object)parsedValue; /*
TriggerError("Not a valid number"); * Ushort
} */
// ulong else if (typeof(T) == typeof(ushort))
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) {
{ return ((ushort)(object)arg).ToString(Format, Culture);
if (ulong.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue)) }
return (T)(object)parsedValue; else if (typeof(T) == typeof(ushort?))
TriggerError("Not a valid number"); {
} return ((ushort?)(object)arg).Value.ToString(Format, Culture);
// float }
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?)) /*
{ * Int
if (float.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) */
return (T)(object)parsedValue; else if (typeof(T) == typeof(int))
TriggerError("Not a valid number"); {
} return ((int)(object)arg).ToString(Format, Culture);
// double }
else if (typeof(T) == typeof(double) || typeof(T) == typeof(double?)) else if (typeof(T) == typeof(int?))
{ {
if (double.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) return ((int?)(object)arg).Value.ToString(Format, Culture);
return (T)(object)parsedValue; }
TriggerError("Not a valid number"); /*
} * Uint
// decimal */
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?)) else if (typeof(T) == typeof(uint))
{ {
if (decimal.TryParse(value, NumberStyles.Any, Culture, out var parsedValue)) return ((uint)(object)arg).ToString(Format, Culture);
return (T)(object)parsedValue; }
TriggerError("Not a valid number"); else if (typeof(T) == typeof(uint?))
} {
// guid return ((uint?)(object)arg).Value.ToString(Format, Culture);
else if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?)) }
{ /*
if (Guid.TryParse(value, out var parsedValue)) * Long
return (T)(object)parsedValue; */
TriggerError("Not a valid GUID"); else if (typeof(T) == typeof(long))
} {
// enum return ((long)(object)arg).ToString(Format, Culture);
else if (IsNullableEnum(typeof(T))) }
{ else if (typeof(T) == typeof(long?))
var enum_type = Nullable.GetUnderlyingType(typeof(T)); {
if (Enum.TryParse(enum_type, value, out var parsedValue)) return ((long?)(object)arg).Value.ToString(Format, Culture);
return (T)parsedValue; }
TriggerError("Not a value of " + enum_type.Name); /*
} * Ulong
else if (typeof(T).IsEnum) */
{ else if (typeof(T) == typeof(ulong))
if (Enum.TryParse(typeof(T), value, out var parsedValue)) {
return (T)parsedValue; return ((ulong)(object)arg).ToString(Format, Culture);
TriggerError("Not a value of " + typeof(T).Name); }
} else if (typeof(T) == typeof(ulong?))
// datetime {
else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?)) return ((ulong?)(object)arg).Value.ToString(Format, Culture);
{ }
try /*
{ * Float
return (T)(object)DateTime.ParseExact(value, Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); */
} else if (typeof(T) == typeof(float))
catch (FormatException) {
{ return ((float)(object)arg).ToString(Format, Culture);
TriggerError("Not a valid date time"); }
} else if (typeof(T) == typeof(float?))
} {
// timespan return ((float?)(object)arg).Value.ToString(Format, Culture);
else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?)) }
{ /*
try * Double
{ */
return (T)(object)TimeSpan.ParseExact(value, Format ?? DefaultTimeSpanFormat, Culture); else if (typeof(T) == typeof(double))
} {
catch (FormatException) return ((double)(object)arg).ToString(Format, Culture);
{ }
TriggerError("Not a valid time span"); else if (typeof(T) == typeof(double?))
} {
} return ((double?)(object)arg).Value.ToString(Format, Culture);
else }
{ /*
TriggerError($"Conversion to type {typeof(T)} not implemented"); * Decimal
} */
} else if (typeof(T) == typeof(decimal))
catch (Exception e) {
{ return ((decimal)(object)arg).ToString(Format, Culture);
TriggerError("Conversion error: " + e.Message); }
} 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) public static bool IsNullableEnum(Type t)
{ {
if (arg == null) var u = Nullable.GetUnderlyingType(t);
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) return (u is not null) && u.IsEnum;
{ }
var u = Nullable.GetUnderlyingType(t);
return (u != 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; using System.Globalization;
namespace Connected 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>
{
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) protected override T? ConvertValueBack(double value)
{ {
try /*
{ * Double
// double */
if (typeof(T) == typeof(double) || typeof(T) == typeof(double?)) if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
return (T)(object)value; {
// string return (T)(object)value;
else if (typeof(T) == typeof(string)) }
return (T)(object)value.ToString(Culture); /*
// sbyte * String
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?)) */
return (T)(object)System.Convert.ToSByte(value); else if (typeof(T) == typeof(string))
// byte {
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?)) return (T)(object)value.ToString(Culture);
return (T)(object)System.Convert.ToByte(value); }
// short /*
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?)) * Sbyte
return (T)(object)System.Convert.ToInt16(value); */
// ushort else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?)) {
return (T)(object)System.Convert.ToUInt16(value); return (T)(object)System.Convert.ToSByte(value);
// int }
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?)) /*
return (T)(object)System.Convert.ToInt32(value); * Byte
// uint */
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?)) else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
return (T)(object)System.Convert.ToUInt32(value); {
// long return (T)(object)System.Convert.ToByte(value);
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?)) }
return (T)(object)System.Convert.ToInt64(value); /*
// ulong * Short
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?)) */
return (T)(object)System.Convert.ToUInt64(value); else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
// float {
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?)) return (T)(object)System.Convert.ToInt16(value);
return (T)(object)System.Convert.ToSingle(value); }
// decimal /*
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?)) * Ushort
return (T)(object)System.Convert.ToDecimal(value); */
else else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
{ {
TriggerError($"Conversion to type {typeof(T)} not implemented"); return (T)(object)System.Convert.ToUInt16(value);
} }
} /*
catch (Exception e) * Int
{ */
TriggerError("Conversion error: " + e.Message); else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
return default(T); {
} return (T)(object)System.Convert.ToInt32(value);
return default(T); }
} /*
* Uint
private double OnSet(T arg) */
{ else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
if (arg == null) {
return double.NaN; // <-- this catches all nullable values which are null. no nullchecks necessary below! return (T)(object)System.Convert.ToUInt32(value);
try }
{ /*
// double * Long
if (typeof(T) == typeof(double)) */
return (double)(object)arg; else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
else if (typeof(T) == typeof(double?)) {
return ((double?)(object)arg).Value; return (T)(object)System.Convert.ToInt64(value);
// string }
if (typeof(T) == typeof(string)) /*
return double.Parse((string)(object)arg, NumberStyles.Any, Culture); * Ulong
// sbyte */
if (typeof(T) == typeof(sbyte)) else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
return System.Convert.ToDouble((sbyte)(object)arg); {
if (typeof(T) == typeof(sbyte?)) return (T)(object)System.Convert.ToUInt64(value);
return System.Convert.ToDouble(((sbyte?)(object)arg).Value); }
// byte /*
if (typeof(T) == typeof(byte)) * Float
return System.Convert.ToDouble((byte)(object)arg); */
if (typeof(T) == typeof(byte?)) else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
return System.Convert.ToDouble(((byte?)(object)arg).Value); {
// short return (T)(object)System.Convert.ToSingle(value);
if (typeof(T) == typeof(short)) }
return System.Convert.ToDouble((short)(object)arg); /*
if (typeof(T) == typeof(short?)) * Decimal
return System.Convert.ToDouble(((short?)(object)arg).Value); */
// ushort else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
if (typeof(T) == typeof(ushort)) {
return System.Convert.ToDouble((ushort)(object)arg); return (T)(object)System.Convert.ToDecimal(value);
if (typeof(T) == typeof(ushort?)) }
return System.Convert.ToDouble(((ushort?)(object)arg).Value); else
// int {
else if (typeof(T) == typeof(int)) TriggerError($"Conversion to type {typeof(T)} not implemented");
return System.Convert.ToDouble((int)(object)arg); return default;
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;
}
}
#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) #endregion
{ }
// Copyright (c) Michael Borgwardt
var absA = Math.Abs(a);
var absB = Math.Abs(b);
var diff = Math.Abs(a - b);
if (a.Equals(b)) [ExcludeFromCodeCoverage]
{ // shortcut, handles infinities internal static class Number
return true; {
} public static T? To<T>(double d)
else if (a == 0 || b == 0 || absA + absB < MinNormal) {
{ if (typeof(T) == typeof(sbyte) && d >= sbyte.MinValue && sbyte.MaxValue >= d)
// a or b is zero or both are extremely close to it return (T)(object)Convert.ToSByte(d);
// relative error is less meaningful here if (typeof(T) == typeof(byte) && d >= byte.MinValue && byte.MaxValue >= d)
return diff < (epsilon * MinNormal); return (T)(object)Convert.ToByte(d);
} if (typeof(T) == typeof(short) && d >= short.MinValue && short.MaxValue >= d)
else return (T)(object)Convert.ToInt16(d);
{ // use relative error if (typeof(T) == typeof(ushort) && d >= ushort.MinValue && ushort.MaxValue >= d)
return diff / (absA + absB) < epsilon; 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] return default;
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;
}
}
} }

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

Loading…
Cancel
Save