using System.Globalization; namespace Connected; /// /// A universal T to string binding converter /// public class DefaultConverter : ToStringConverter { 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; TriggerError("Not a valid GUID"); } /* * Enum */ else if (IsNullableEnum(typeof(T))) { var enum_type = Nullable.GetUnderlyingType(typeof(T)); if (Enum.TryParse(enum_type, value, out var parsedValue)) return (T)parsedValue; TriggerError("Not a value of " + enum_type.Name); } else if (typeof(T).IsEnum) { if (Enum.TryParse(typeof(T), value, out var parsedValue)) return (T)parsedValue; TriggerError("Not a value of " + typeof(T).Name); } /* * DateTime */ else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?)) { try { return (T)(object)DateTime.ParseExact(value, Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } catch (FormatException) { TriggerError("Not a valid date time"); } } /* * Timespan */ else if (typeof(T) == typeof(TimeSpan) || typeof(T) == typeof(TimeSpan?)) { try { return (T)(object)TimeSpan.ParseExact(value, Format ?? DefaultTimeSpanFormat, Culture); } catch (FormatException) { TriggerError("Not a valid time span"); } } else { TriggerError($"Conversion to type {typeof(T)} not implemented"); } } catch (Exception e) { TriggerError("Conversion error: " + e.Message); } return default; } protected virtual string? ConvertToString(T? arg) { /* * This catches all null values. No additional null checks necessary. */ if (arg is null) return null; try { /* * String */ if (typeof(T) == typeof(string)) { return (string)(object)arg; } /* * Char */ else if (typeof(T) == typeof(char)) { return ((char)(object)arg).ToString(Culture); } else if (typeof(T) == typeof(char?)) { return ((char?)(object)arg).Value.ToString(Culture); } /* * Bool */ else if (typeof(T) == typeof(bool)) { return ((bool)(object)arg).ToString(CultureInfo.InvariantCulture); } else if (typeof(T) == typeof(bool?)) { return ((bool?)(object)arg).Value.ToString(CultureInfo.InvariantCulture); } /* * Sbyte */ else if (typeof(T) == typeof(sbyte)) { return ((sbyte)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(sbyte?)) { return ((sbyte?)(object)arg).Value.ToString(Format, Culture); } /* * Byte */ else if (typeof(T) == typeof(byte)) { return ((byte)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(byte?)) { return ((byte?)(object)arg).Value.ToString(Format, Culture); } /* * Short */ else if (typeof(T) == typeof(short)) { return ((short)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(short?)) { return ((short?)(object)arg).Value.ToString(Format, Culture); } /* * Ushort */ else if (typeof(T) == typeof(ushort)) { return ((ushort)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(ushort?)) { return ((ushort?)(object)arg).Value.ToString(Format, Culture); } /* * Int */ else if (typeof(T) == typeof(int)) { return ((int)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(int?)) { return ((int?)(object)arg).Value.ToString(Format, Culture); } /* * Uint */ else if (typeof(T) == typeof(uint)) { return ((uint)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(uint?)) { return ((uint?)(object)arg).Value.ToString(Format, Culture); } /* * Long */ else if (typeof(T) == typeof(long)) { return ((long)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(long?)) { return ((long?)(object)arg).Value.ToString(Format, Culture); } /* * Ulong */ else if (typeof(T) == typeof(ulong)) { return ((ulong)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(ulong?)) { return ((ulong?)(object)arg).Value.ToString(Format, Culture); } /* * Float */ else if (typeof(T) == typeof(float)) { return ((float)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(float?)) { return ((float?)(object)arg).Value.ToString(Format, Culture); } /* * Double */ else if (typeof(T) == typeof(double)) { return ((double)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(double?)) { return ((double?)(object)arg).Value.ToString(Format, Culture); } /* * Decimal */ else if (typeof(T) == typeof(decimal)) { return ((decimal)(object)arg).ToString(Format, Culture); } else if (typeof(T) == typeof(decimal?)) { return ((decimal?)(object)arg).Value.ToString(Format, Culture); } /* * Guid */ else if (typeof(T) == typeof(Guid)) { var value = (Guid)(object)arg; return value.ToString(); } else if (typeof(T) == typeof(Guid?)) { var value = (Guid?)(object)arg; return value.Value.ToString(); } /* * Enum */ else if (IsNullableEnum(typeof(T))) { var value = (Enum)(object)arg; return value.ToString(); } else if (typeof(T).IsEnum) { var value = (Enum)(object)arg; return value.ToString(); } /* * DateTime */ else if (typeof(T) == typeof(DateTime)) { var value = (DateTime)(object)arg; return value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } else if (typeof(T) == typeof(DateTime?)) { var value = (DateTime?)(object)arg; return value.Value.ToString(Format ?? Culture.DateTimeFormat.ShortDatePattern, Culture); } /* * Timespan */ else if (typeof(T) == typeof(TimeSpan)) { var value = (TimeSpan)(object)arg; return value.ToString(Format ?? DefaultTimeSpanFormat, Culture); } else if (typeof(T) == typeof(TimeSpan?)) { var value = (TimeSpan?)(object)arg; return value.Value.ToString(Format ?? DefaultTimeSpanFormat, Culture); } return arg.ToString(); } catch (FormatException e) { TriggerError("Conversion error: " + e.Message); return null; } } public static bool IsNullableEnum(Type t) { var u = Nullable.GetUnderlyingType(t); return (u is not null) && u.IsEnum; } }