[WIP] Refactor converters and dependant components.
This commit is contained in:
parent
6f91dacb0c
commit
be914e4b2e
226
.editorconfig
Normal file
226
.editorconfig
Normal file
@ -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
|
||||
namespace Connected;
|
||||
|
||||
public enum InputType
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ 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;
|
||||
|
@ -1,54 +1,8 @@
|
||||
using Connected.Extensions;
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Connected
|
||||
namespace Connected;
|
||||
|
||||
public static class Converters
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 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?>
|
||||
{
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return OnGet(value);
|
||||
}
|
||||
|
||||
private DateTime OnGet(string arg)
|
||||
{
|
||||
try
|
||||
{
|
||||
return DateTime.ParseExact(arg, DateFormat, Culture);
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
TriggerError(e.Message);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
public DateConverter(string format = "yyyy-MM-dd") => DateFormat = format;
|
||||
|
||||
protected override string? ConvertValue(DateTime value) => value.ToString(DateFormat, Culture);
|
||||
|
||||
protected override DateTime ConvertValueBack(string? value) => DateTime.ParseExact(value, DateFormat, Culture);
|
||||
}
|
@ -1,156 +1,200 @@
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// A universal T to string binding converter
|
||||
/// </summary>
|
||||
public class DefaultConverter<T> : ToStringConverter<T>
|
||||
{
|
||||
|
||||
public DefaultConverter()
|
||||
{
|
||||
}
|
||||
|
||||
protected override string? ConvertValue(T? value)
|
||||
{
|
||||
return ConvertToString(value);
|
||||
}
|
||||
|
||||
protected override T? ConvertValueBack(string? value)
|
||||
{
|
||||
return ConvertFromString(value);
|
||||
}
|
||||
protected override T? ConvertValueBack(string? value) => ConvertFromString(value);
|
||||
|
||||
public string DefaultTimeSpanFormat { get; set; } = "c";
|
||||
|
||||
protected virtual T ConvertFromString(string value)
|
||||
protected virtual T? ConvertFromString(string? value)
|
||||
{
|
||||
try
|
||||
{
|
||||
// string
|
||||
if (typeof(T) == typeof(string))
|
||||
return (T)(object)value;
|
||||
|
||||
// this is important, or otherwise all the TryParse down there might fail.
|
||||
/*
|
||||
* This is important, or otherwise all the TryParse down there might fail.
|
||||
*/
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return default(T);
|
||||
// char
|
||||
{
|
||||
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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* DateTime
|
||||
*/
|
||||
else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?))
|
||||
{
|
||||
try
|
||||
@ -162,7 +206,9 @@ namespace Connected
|
||||
TriggerError("Not a valid date time");
|
||||
}
|
||||
}
|
||||
// timespan
|
||||
/*
|
||||
* Timespan
|
||||
*/
|
||||
else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?))
|
||||
{
|
||||
try
|
||||
@ -184,84 +230,172 @@ namespace Connected
|
||||
TriggerError("Conversion error: " + e.Message);
|
||||
}
|
||||
|
||||
return default(T);
|
||||
return default;
|
||||
}
|
||||
|
||||
protected virtual string ConvertToString(T arg)
|
||||
protected virtual string? ConvertToString(T? arg)
|
||||
{
|
||||
if (arg == null)
|
||||
return null; // <-- this catches all nullable values which are null. no nullchecks necessary below!
|
||||
/*
|
||||
* This catches all null values. No additional null checks necessary.
|
||||
*/
|
||||
if (arg is null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// string
|
||||
/*
|
||||
* String
|
||||
*/
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
return (string)(object)arg;
|
||||
// char
|
||||
if (typeof(T) == typeof(char))
|
||||
}
|
||||
/*
|
||||
* Char
|
||||
*/
|
||||
else if (typeof(T) == typeof(char))
|
||||
{
|
||||
return ((char)(object)arg).ToString(Culture);
|
||||
if (typeof(T) == typeof(char?))
|
||||
}
|
||||
else if (typeof(T) == typeof(char?))
|
||||
{
|
||||
return ((char?)(object)arg).Value.ToString(Culture);
|
||||
// bool
|
||||
if (typeof(T) == typeof(bool))
|
||||
}
|
||||
/*
|
||||
* Bool
|
||||
*/
|
||||
else if (typeof(T) == typeof(bool))
|
||||
{
|
||||
return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture);
|
||||
if (typeof(T) == typeof(bool?))
|
||||
}
|
||||
else if (typeof(T) == typeof(bool?))
|
||||
{
|
||||
return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture);
|
||||
// sbyte
|
||||
if (typeof(T) == typeof(sbyte))
|
||||
}
|
||||
/*
|
||||
* Sbyte
|
||||
*/
|
||||
else if (typeof(T) == typeof(sbyte))
|
||||
{
|
||||
return ((sbyte)(object)arg).ToString(Format, Culture);
|
||||
if (typeof(T) == typeof(sbyte?))
|
||||
}
|
||||
else if (typeof(T) == typeof(sbyte?))
|
||||
{
|
||||
return ((sbyte?)(object)arg).Value.ToString(Format, Culture);
|
||||
// byte
|
||||
if (typeof(T) == typeof(byte))
|
||||
}
|
||||
/*
|
||||
* Byte
|
||||
*/
|
||||
else if (typeof(T) == typeof(byte))
|
||||
{
|
||||
return ((byte)(object)arg).ToString(Format, Culture);
|
||||
if (typeof(T) == typeof(byte?))
|
||||
}
|
||||
else if (typeof(T) == typeof(byte?))
|
||||
{
|
||||
return ((byte?)(object)arg).Value.ToString(Format, Culture);
|
||||
// short
|
||||
if (typeof(T) == typeof(short))
|
||||
}
|
||||
/*
|
||||
* Short
|
||||
*/
|
||||
else if (typeof(T) == typeof(short))
|
||||
{
|
||||
return ((short)(object)arg).ToString(Format, Culture);
|
||||
if (typeof(T) == typeof(short?))
|
||||
}
|
||||
else if (typeof(T) == typeof(short?))
|
||||
{
|
||||
return ((short?)(object)arg).Value.ToString(Format, Culture);
|
||||
// ushort
|
||||
if (typeof(T) == typeof(ushort))
|
||||
}
|
||||
/*
|
||||
* Ushort
|
||||
*/
|
||||
else if (typeof(T) == typeof(ushort))
|
||||
{
|
||||
return ((ushort)(object)arg).ToString(Format, Culture);
|
||||
if (typeof(T) == typeof(ushort?))
|
||||
}
|
||||
else if (typeof(T) == typeof(ushort?))
|
||||
{
|
||||
return ((ushort?)(object)arg).Value.ToString(Format, Culture);
|
||||
// int
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
/*
|
||||
* Guid
|
||||
*/
|
||||
else if (typeof(T) == typeof(Guid))
|
||||
{
|
||||
var value = (Guid)(object)arg;
|
||||
@ -272,7 +406,9 @@ namespace Connected
|
||||
var value = (Guid?)(object)arg;
|
||||
return value.Value.ToString();
|
||||
}
|
||||
// enum
|
||||
/*
|
||||
* Enum
|
||||
*/
|
||||
else if (IsNullableEnum(typeof(T)))
|
||||
{
|
||||
var value = (Enum)(object)arg;
|
||||
@ -283,7 +419,9 @@ namespace Connected
|
||||
var value = (Enum)(object)arg;
|
||||
return value.ToString();
|
||||
}
|
||||
// datetime
|
||||
/*
|
||||
* DateTime
|
||||
*/
|
||||
else if (typeof(T) == typeof(DateTime))
|
||||
{
|
||||
var value = (DateTime)(object)arg;
|
||||
@ -294,7 +432,9 @@ namespace Connected
|
||||
var value = (DateTime?)(object)arg;
|
||||
return value.Value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture);
|
||||
}
|
||||
// timespan
|
||||
/*
|
||||
* Timespan
|
||||
*/
|
||||
else if (typeof(T) == typeof(TimeSpan))
|
||||
{
|
||||
var value = (TimeSpan)(object)arg;
|
||||
@ -305,6 +445,7 @@ namespace Connected
|
||||
var value = (TimeSpan?)(object)arg;
|
||||
return value.Value.ToString(Format ?? DefaultTimeSpanFormat, Culture);
|
||||
}
|
||||
|
||||
return arg.ToString();
|
||||
}
|
||||
catch (FormatException e)
|
||||
@ -317,7 +458,7 @@ namespace Connected
|
||||
public static bool IsNullableEnum(Type t)
|
||||
{
|
||||
var u = Nullable.GetUnderlyingType(t);
|
||||
return (u != null) && u.IsEnum;
|
||||
}
|
||||
|
||||
return (u is not null) && u.IsEnum;
|
||||
}
|
||||
}
|
||||
|
17
Utilities/BindingConverters/IsoDateConverter.cs
Normal file
17
Utilities/BindingConverters/IsoDateConverter.cs
Normal file
@ -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,199 +1,295 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
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>
|
||||
{
|
||||
|
||||
/// <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()
|
||||
{
|
||||
}
|
||||
|
||||
protected override double ConvertValue(T? value)
|
||||
{
|
||||
return OnSet(value);
|
||||
if (value is null)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
protected override T? ConvertValueBack(double value)
|
||||
/*
|
||||
* Double
|
||||
*/
|
||||
else if (typeof(T) == typeof(double))
|
||||
{
|
||||
return OnGet(value);
|
||||
return (double)(object)value;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((int)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(int?))
|
||||
return System.Convert.ToDouble(((int?)(object)arg).Value);
|
||||
// uint
|
||||
{
|
||||
return System.Convert.ToDouble(((int?)(object)value).Value);
|
||||
}
|
||||
/*
|
||||
* Uint
|
||||
*/
|
||||
else if (typeof(T) == typeof(uint))
|
||||
return System.Convert.ToDouble((uint)(object)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((uint)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(uint?))
|
||||
return System.Convert.ToDouble(((uint?)(object)arg).Value);
|
||||
// long
|
||||
{
|
||||
return System.Convert.ToDouble(((uint?)(object)value).Value);
|
||||
}
|
||||
/*
|
||||
* Long
|
||||
*/
|
||||
else if (typeof(T) == typeof(long))
|
||||
return System.Convert.ToDouble((long)(object)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((long)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(long?))
|
||||
return System.Convert.ToDouble(((long?)(object)arg).Value);
|
||||
// ulong
|
||||
{
|
||||
return System.Convert.ToDouble(((long?)(object)value).Value);
|
||||
}
|
||||
/*
|
||||
* Ulong
|
||||
*/
|
||||
else if (typeof(T) == typeof(ulong))
|
||||
return System.Convert.ToDouble((ulong)(object)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((ulong)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(ulong?))
|
||||
return System.Convert.ToDouble(((ulong?)(object)arg).Value);
|
||||
// float
|
||||
{
|
||||
return System.Convert.ToDouble(((ulong?)(object)value).Value);
|
||||
}
|
||||
/*
|
||||
* Float
|
||||
*/
|
||||
else if (typeof(T) == typeof(float))
|
||||
return System.Convert.ToDouble((float)(object)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((float)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(float?))
|
||||
return System.Convert.ToDouble(((float?)(object)arg).Value);
|
||||
// decimal
|
||||
{
|
||||
return System.Convert.ToDouble(((float?)(object)value).Value);
|
||||
}
|
||||
/*
|
||||
* Deimal
|
||||
*/
|
||||
else if (typeof(T) == typeof(decimal))
|
||||
return System.Convert.ToDouble((decimal)(object)arg);
|
||||
{
|
||||
return System.Convert.ToDouble((decimal)(object)value);
|
||||
}
|
||||
else if (typeof(T) == typeof(decimal?))
|
||||
return System.Convert.ToDouble(((decimal?)(object)arg).Value);
|
||||
{
|
||||
return System.Convert.ToDouble(((decimal?)(object)value).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
TriggerError("Unable to convert to double from type " + typeof(T).Name);
|
||||
return double.NaN;
|
||||
}
|
||||
}
|
||||
catch (FormatException e)
|
||||
|
||||
protected override T? ConvertValueBack(double value)
|
||||
{
|
||||
TriggerError("Conversion error: " + e.Message);
|
||||
return double.NaN;
|
||||
/*
|
||||
* 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
|
||||
|
||||
const double MinNormal = 2.2250738585072014E-308d;
|
||||
private const double MinNormal = 2.2250738585072014E-308d;
|
||||
|
||||
public static bool AreEqual(double a, double b, double epsilon = MinNormal)
|
||||
{
|
||||
// Copyright (c) Michael Borgwardt
|
||||
/*
|
||||
* 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))
|
||||
{ // 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);
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
/*
|
||||
* Use relative error
|
||||
*/
|
||||
return diff / (absA + absB) < epsilon;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class Num
|
||||
{
|
||||
public static T To<T>(double d)
|
||||
[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);
|
||||
@ -239,6 +335,7 @@ namespace Connected
|
||||
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)
|
||||
@ -287,7 +384,7 @@ namespace Connected
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
public static string Join(string? valueStart, string? valueEnd) => string.IsNullOrEmpty(valueStart) && string.IsNullOrEmpty(valueEnd) ? string.Empty : $"[{valueStart};{valueEnd}]";
|
||||
|
||||
return 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 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…
x
Reference in New Issue
Block a user